Generate Go code to convert database rows into arbitrary structs.
Go
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.

README.md

scaneo

Build Status Coverage Status

Generate Go code to convert database rows into arbitrary structs. Works with any database driver. Don't have to worry about database columns and struct names matching or tagging structs. No ORM magic.

Installation

Install or upgrade Scaneo with this command.

go get -u github.com/variadico/scaneo

Otherwise, check out the releases page.

Usage

scaneo [options] paths...

Options

-o, -output
    Set the name of the generated file. Default is scans.go.

-p, -package
    Set the package name for the generated file. Default is current
    directory name.

-u, -unexport
    Generate unexported functions. Default is export all.

-w, -whitelist
    Only include structs specified in case-sensitive, comma-delimited
    string.

-v, -version
    Print version and exit.

-h, -help
    Print help and exit.

3-Step Tutorial

First, we start with a file that looks like this, called tables.go.

package models

import (
	"time"

	"github.com/lib/pq"
)

type Post struct {
	ID        int
	Created   time.Time
	Published pq.NullTime
	Draft     bool
	Title     string
	Body      string
}

Second, run scaneo tables.go. This will create a new file called scans.go, which looks like this.

// DON'T EDIT *** generated by scaneo *** DON'T EDIT //

package models

import "database/sql"

func ScanPost(r *sql.Row) (Post, error) {
	var s Post
	if err := r.Scan(
		&s.ID,
		&s.Created,
		&s.Published,
		&s.Draft,
		&s.Title,
		&s.Body,
	); err != nil {
		return Post{}, err
	}
	return s, nil
}
func ScanPosts(rs *sql.Rows) ([]Post, error) {
	structs := make([]Post, 0, 16)
	var err error
	for rs.Next() {
		var s Post
		if err = rs.Scan(
			&s.ID,
			&s.Created,
			&s.Published,
			&s.Draft,
			&s.Title,
			&s.Body,
		); err != nil {
			return nil, err
		}
		structs = append(structs, s)
	}
	return structs, nil
}

Third, call those functions from other parts of your code, like this.

func serveHome(resp http.ResponseWriter, req *http.Request) {
	rows, err := db.Query("select * from post")
	if err != nil {
		log.Println(err)
		return
	}

	posts, err := models.ScanPosts(rows) // ScanPosts was auto-generated!
	if err != nil {
		log.Println(err)
	}

	// ... send posts to template or whatever...
}

Go Generate

If you want to use scaneo with go generate, then just add this comment to the top of tables.go.

//go:generate scaneo $GOFILE

package models
// ... rest of code...

Now you can call go generate in package models and scans.go will be created.

FAQ

Why did you write this instead of using sqlx, modl, gorm, gorp, etc?

  1. Didn't know which one I should learn. Already knew database/sql.
  2. All I wanted was structs from the database.
  3. I can write SQL.

In addition, I wasn't entirely sure how much more convenient an ORM would be in contrast to writing the SQL myself. After reading some usage examples, it kind of felt like I was writing SQL anyway.

// github.com/go-gorp/gorp
dbMap.Select(&users, "SELECT * FROM users WHERE last_name = ? OR age < ? ORDER BY age DESC LIMIT 2", "Doe", 12)

// github.com/eaigner/hood
hd.Where("last_name", "=", "Doe").Or("age", "<", 12).OrderBy("age").Limit(2).Find(&users)

// github.com/jmoiron/sqlx
db.Select(&people, "SELECT * FROM person ORDER BY first_name ASC")

// github.com/astaxie/beedb
orm.Where("last_name = ? OR age < ?", "Doe", 12).Limit(2).OrderBy("age").FindAll(&users)

Do my table columns have to match my struct field names?

Nope. The names don't actually matter at all. However, what does matter is the order in which you declare the types in your struct. That has to match the database table. So if your table looks like this:

 user_id | first_name | last_name
---------+------------+-----------
       1 | Silvio     | Rodríguez

then, your struct field names must follow the same order, like this.

type User struct {
	ID        int
	FirstName string
	LastName  string
}