Full Code of drone/sqlgen for AI

master 39ee509bb576 cached
30 files
58.2 KB
18.3k tokens
189 symbols
1 requests
Download .txt
Repository: drone/sqlgen
Branch: master
Commit: 39ee509bb576
Files: 30
Total size: 58.2 KB

Directory structure:
gitextract_ds8o5qgz/

├── .gitignore
├── LICENSE
├── README.md
├── bench/
│   ├── type.go
│   ├── type_sql.go
│   └── type_test.go
├── demo/
│   ├── hook.go
│   ├── hook_sql.go
│   ├── issue.go
│   ├── issue_sql.go
│   ├── user.go
│   └── user_sql.go
├── fmt.go
├── gen.go
├── gen_funcs.go
├── gen_schema.go
├── parse/
│   ├── const.go
│   ├── node.go
│   ├── parse.go
│   ├── tag.go
│   └── tag_test.go
├── schema/
│   ├── base.go
│   ├── dialect.go
│   ├── dialect_mysql.go
│   ├── dialect_postgres.go
│   ├── dialect_sqlite.go
│   ├── helper.go
│   └── schema.go
├── tmpl.go
└── util.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
sqlgen
*.sqlite
*.txt
_docs


================================================
FILE: LICENSE
================================================
Copyright (c) 2015, drone.io
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



================================================
FILE: README.md
================================================
**sqlgen** generates SQL statements and database helper functions from your Go structs. It can be used in place of a simple ORM or hand-written SQL. See the [demo](https://github.com/drone/sqlgen/tree/master/demo) directory for examples.

### Install

Install or upgrade with this command:

```
go get -u github.com/drone/sqlgen
```

### Usage

```
Usage of sqlgen:
  -type string
    	type to generate; required
  -file string
    	input file name; required
  -o string
    	output file name
  -pkg string
    	output package name
  -db string
    	sql dialect; sqlite, postgres, mysql
  -schema
    	generate sql schema and queries; default true
  -funcs
    	generate sql helper functions; default true
```

### Tutorial

First, let's start with a simple `User` struct in `user.go`:

```Go
type User struct {
	ID     int64
	Login  string
	Email  string
}
```

We can run the following command:

```
sqlgen -file user.go -type User -pkg demo
```

The tool outputs the following generated code:

```Go
func ScanUser(row *sql.Row) (*User, error) {
	var v0 int64
	var v1 string
	var v2 string

	err := row.Scan(
		&v0,
		&v1,
		&v2,
	)
	if err != nil {
		return nil, err
	}

	v := &User{}
	v.ID = v0
	v.Login = v1
	v.Email = v2

	return v, nil
}

const CreateUserStmt = `
CREATE TABLE IF NOT EXISTS users (
 user_id     INTEGER
,user_login  TEXT
,user_email  TEXT
);
`

const SelectUserStmt = `
SELECT 
 user_id
,user_login
,user_email
FROM users 
`

const SelectUserRangeStmt = `
SELECT 
 user_id
,user_login
,user_email
FROM users 
LIMIT ? OFFSET ?
`


// more functions and sql statements not displayed
```

This is a great start, but what if we want to specify primary keys, column sizes and more? This may be acheived by annotating your code using Go tags. For example, we can tag the `ID` field to indicate it is a primary key and will auto increment:

```diff
type User struct {
-   ID      int64
+   ID      int64  `sql:"pk: true, auto: true"`
    Login   string
    Email   string
}
```

This information allows the tool to generate smarter SQL statements:

```diff
CREATE TABLE IF NOT EXISTS users (
-user_id     INTEGER
+user_id     INTEGER PRIMARY KEY AUTOINCREMENT
,user_login  TEXT
,user_email  TEXT
);
```

Including SQL statements to select, insert, update and delete data using the primary key:

```Go
const SelectUserPkeyStmt = `
SELECT 
 user_id
,user_login
,user_email
WHERE user_id=?
`

const UpdateUserPkeyStmt = `
UPDATE users SET 
 user_id=?
,user_login=?
,user_email=?
WHERE user_id=?
`

const DeleteUserPkeyStmt = `
DELETE FROM users 
WHERE user_id=?
`
```

We can take this one step further and annotate indexes. In our example, we probably want to make sure the `user_login` field has a unique index:

```diff
type User struct {
    ID      int64  `sql:"pk: true, auto: true"`
-   Login   string
+   Login   string `sql:"unique: user_login"`
    Email   string
}
```

This information instructs the tool to generate the following:


```Go
const CreateUserLogin = `
CREATE UNIQUE INDEX IF NOT EXISTS user_login ON users (user_login)
```

The tool also assumes that we probably intend to fetch data from the database using this index. The tool will therefore automatically generate the following queries:

```Go
const SelectUserLoginStmt = `
SELECT 
 user_id
,user_login
,user_email
WHERE user_login=?
`

const UpdateUserLoginStmt = `
UPDATE users SET 
 user_id=?
,user_login=?
,user_email=?
WHERE user_login=?
`

const DeleteUserLoginStmt = `
DELETE FROM users 
WHERE user_login=?
`
```

### Nesting

Nested Go structures can be flattened into a single database table. As an example, we have a `User` and `Address` with a one-to-one relationship. In some cases, we may prefer to de-normalize our data and store in a single table, avoiding un-necessary joins.

```diff
type User struct {
    ID     int64  `sql:"pk: true"`
    Login  string
    Email  string
+   Addr   *Address
}

type Address struct {
    City   string
    State  string
    Zip    string `sql:"index: user_zip"`
}
```

The above relationship is flattened into a single table (see below). When the data is retrieved from the database the nested structure is restored.

```sql
CREATE TALBE IF NOT EXISTS users (
 user_id         INTEGER PRIMARY KEY AUTO_INCREMENT
,user_login      TEXT
,user_email      TEXT
,user_addr_city  TEXT
,user_addr_state TEXT
,user_addr_zip   TEXT
);
```

### JSON Encoding

Some types in your struct may not have native equivalents in your database such as `[]string`. These values can be marshaled and stored as JSON in the database.

```diff
type User struct {
    ID     int64  `sql:"pk: true"`
    Login  string
    Email  string
+   Label  []string `sql:"encode: json"
}
```

### Dialects

You may specify one of the following SQL dialects when generating your code: `postgres`, `mysql` and `sqlite`. The default value is `sqlite`.

```
sqlgen -file user.go -type User -pkg demo -db postgres
```


### Go Generate

Example use with `go:generate`:

```Go
package demo

//go:generate sqlgen -file user.go -type User -pkg demo -o user_sql.go

type User struct {
    ID     int64  `sql:"pk: true, auto: true"`
    Login  string `sql:"unique: user_login"`
    Email  string `sql:"size: 1024"`
    Avatar string
}
```

### Benchmarks

This tool demonstrates performance gains, albeit small, over light-weight ORM packages such as `sqlx` and `meddler`. Over time I plan to expand the benchmarks to include additional ORM packages.

To run the project benchmarks:

```
go get ./...
go generate ./...
go build
cd bench
go test -bench=Bench
```

Example selecing a single row:

```
BenchmarkMeddlerRow-4      30000        42773 ns/op
BenchmarkSqlxRow-4         30000        41554 ns/op
BenchmarkSqlgenRow-4       50000        39664 ns/op

```

Selecting multiple rows:

```
BenchmarkMeddlerRows-4      2000      1025218 ns/op
BenchmarkSqlxRows-4         2000       807213 ns/op
BenchmarkSqlgenRows-4       2000       700673 ns/op
```


#### Credits

This tool was inspired by [scaneo](https://github.com/variadico/scaneo).


================================================
FILE: bench/type.go
================================================
package bench

//go:generate ../sqlgen -file type.go -type User -pkg bench -o type_sql.go

type User struct {
	ID      int64  `sql:"pk: true, auto: true"   meddler:"user_id,pk"   db:"user_id"`
	Name    string `sql:"unique: user_name"      meddler:"user_name"    db:"user_name"`
	Pass    string `sql:""                       meddler:"user_pass"    db:"user_pass"`
	Email   string `sql:"unique: user_email"     meddler:"user_email"   db:"user_email"`
	Active  bool   `sql:"index:  user_active"    meddler:"user_active"  db:"user_active"`
	Created int64  `sql:""                       meddler:"user_created" db:"user_created"`
	Updated int64  `sql:""                       meddler:"user_updated" db:"user_updated"`
}


================================================
FILE: bench/type_sql.go
================================================
package bench

// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.

import (
	"database/sql"
)

func ScanUser(row *sql.Row) (*User, error) {
	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 int64
	var v6 int64

	err := row.Scan(
		&v0,
		&v1,
		&v2,
		&v3,
		&v4,
		&v5,
		&v6,
	)
	if err != nil {
		return nil, err
	}

	v := &User{}
	v.ID = v0
	v.Name = v1
	v.Pass = v2
	v.Email = v3
	v.Active = v4
	v.Created = v5
	v.Updated = v6

	return v, nil
}

func ScanUsers(rows *sql.Rows) ([]*User, error) {
	var err error
	var vv []*User

	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 int64
	var v6 int64

	for rows.Next() {
		err = rows.Scan(
			&v0,
			&v1,
			&v2,
			&v3,
			&v4,
			&v5,
			&v6,
		)
		if err != nil {
			return vv, err
		}

		v := &User{}
		v.ID = v0
		v.Name = v1
		v.Pass = v2
		v.Email = v3
		v.Active = v4
		v.Created = v5
		v.Updated = v6

		vv = append(vv, v)
	}
	return vv, rows.Err()
}

func SliceUser(v *User) []interface{} {
	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 int64
	var v6 int64

	v0 = v.ID
	v1 = v.Name
	v2 = v.Pass
	v3 = v.Email
	v4 = v.Active
	v5 = v.Created
	v6 = v.Updated

	return []interface{}{
		v0,
		v1,
		v2,
		v3,
		v4,
		v5,
		v6,
	}
}

func SelectUser(db *sql.DB, query string, args ...interface{}) (*User, error) {
	row := db.QueryRow(query, args...)
	return ScanUser(row)
}

func SelectUsers(db *sql.DB, query string, args ...interface{}) ([]*User, error) {
	rows, err := db.Query(query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return ScanUsers(rows)
}

func InsertUser(db *sql.DB, query string, v *User) error {

	res, err := db.Exec(query, SliceUser(v)[1:]...)
	if err != nil {
		return err
	}

	v.ID, err = res.LastInsertId()
	return err
}

func UpdateUser(db *sql.DB, query string, v *User) error {

	args := SliceUser(v)[1:]
	args = append(args, v.ID)
	_, err := db.Exec(query, args...)
	return err
}

const CreateUserStmt = `
CREATE TABLE IF NOT EXISTS users (
 user_id      INTEGER PRIMARY KEY AUTOINCREMENT
,user_name    TEXT
,user_pass    TEXT
,user_email   TEXT
,user_active  BOOLEAN
,user_created INTEGER
,user_updated INTEGER
);
`

const InsertUserStmt = `
INSERT INTO users (
 user_name
,user_pass
,user_email
,user_active
,user_created
,user_updated
) VALUES (?,?,?,?,?,?)
`

const SelectUserStmt = `
SELECT 
 user_id
,user_name
,user_pass
,user_email
,user_active
,user_created
,user_updated
FROM users 
`

const SelectUserRangeStmt = `
SELECT 
 user_id
,user_name
,user_pass
,user_email
,user_active
,user_created
,user_updated
FROM users 
LIMIT ? OFFSET ?
`

const SelectUserCountStmt = `
SELECT count(1)
FROM users 
`

const SelectUserPkeyStmt = `
SELECT 
 user_id
,user_name
,user_pass
,user_email
,user_active
,user_created
,user_updated
FROM users 
WHERE user_id=?
`

const UpdateUserPkeyStmt = `
UPDATE users SET 
 user_id=?
,user_name=?
,user_pass=?
,user_email=?
,user_active=?
,user_created=?
,user_updated=? 
WHERE user_id=?
`

const DeleteUserPkeyStmt = `
DELETE FROM users 
WHERE user_id=?
`

const CreateUserNameStmt = `
CREATE UNIQUE INDEX IF NOT EXISTS user_name ON users (user_name)
`

const SelectUserNameStmt = `
SELECT 
 user_id
,user_name
,user_pass
,user_email
,user_active
,user_created
,user_updated
FROM users 
WHERE user_name=?
`

const UpdateUserNameStmt = `
UPDATE users SET 
 user_id=?
,user_name=?
,user_pass=?
,user_email=?
,user_active=?
,user_created=?
,user_updated=? 
WHERE user_name=?
`

const DeleteUserNameStmt = `
DELETE FROM users 
WHERE user_name=?
`

const CreateUserEmailStmt = `
CREATE UNIQUE INDEX IF NOT EXISTS user_email ON users (user_email)
`

const SelectUserEmailStmt = `
SELECT 
 user_id
,user_name
,user_pass
,user_email
,user_active
,user_created
,user_updated
FROM users 
WHERE user_email=?
`

const UpdateUserEmailStmt = `
UPDATE users SET 
 user_id=?
,user_name=?
,user_pass=?
,user_email=?
,user_active=?
,user_created=?
,user_updated=? 
WHERE user_email=?
`

const DeleteUserEmailStmt = `
DELETE FROM users 
WHERE user_email=?
`


================================================
FILE: bench/type_test.go
================================================
package bench

import (
	"database/sql"
	"testing"
	"time"

	"github.com/Pallinder/go-randomdata"
	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"
	"github.com/russross/meddler"
)

var db *sql.DB
var dbx *sqlx.DB

func init() {
	var err error
	db, err = sql.Open("sqlite3", ":memory:")
	if err != nil {
		panic(err)
	}
	db.Exec("DROP TABLE users;")
	dbx = sqlx.NewDb(db, "sqlite3")

	ddl := []string{CreateUserStmt}
	for _, stmt := range ddl {
		_, err = db.Exec(stmt)
		if err != nil {
			panic(err)
		}
	}

	for i := 0; i < 100; i++ {
		user := &User{}
		user.Name = randomdata.FullName(randomdata.RandomGender)
		user.Email = randomdata.Email()
		user.Pass = "pa55word"
		user.Created = time.Now().Unix()
		user.Updated = time.Now().Unix()

		err := InsertUser(db, InsertUserStmt, user)
		if err != nil {
			panic(err)
		}
	}
}

var result *User
var results []*User

func BenchmarkMeddlerRow(b *testing.B) {
	var user *User
	var err error

	for n := 0; n < b.N; n++ {
		user = &User{}
		err = meddler.QueryRow(db, user, SelectUserPkeyStmt, 1)
		if err != nil {
			panic(err)
		}
	}
	result = user
}

func BenchmarkMeddlerRows(b *testing.B) {
	var users []*User
	var err error

	for n := 0; n < b.N; n++ {
		err = meddler.QueryAll(db, &users, SelectUserStmt)
		if err != nil {
			panic(err)
		}
	}
	results = users
}

func BenchmarkSqlxRow(b *testing.B) {
	var user *User
	var err error

	for n := 0; n < b.N; n++ {
		user = &User{}
		err = dbx.Get(user, SelectUserPkeyStmt, 1)
		if err != nil {
			panic(err)
		}
	}
	result = user
}

func BenchmarkSqlxRows(b *testing.B) {
	var users []*User
	var err error

	for n := 0; n < b.N; n++ {
		err = dbx.Select(&users, SelectUserStmt)
		if err != nil {
			panic(err)
		}
	}
	results = users
}

func BenchmarkSqlgenRow(b *testing.B) {
	var user *User
	var err error

	for n := 0; n < b.N; n++ {
		user, err = SelectUser(db, SelectUserPkeyStmt, 1)
		if err != nil {
			panic(err)
		}
	}
	result = user
}

func BenchmarkSqlgenRows(b *testing.B) {
	var users []*User
	var err error

	for n := 0; n < b.N; n++ {
		users, err = SelectUsers(db, SelectUserStmt)
		if err != nil {
			panic(err)
		}
	}
	results = users
}


================================================
FILE: demo/hook.go
================================================
package demo

//go:generate ../sqlgen -file hook.go -type Hook -pkg demo -o hook_sql.go -db mysql

type Hook struct {
	ID         int64 `sql:"pk: true, auto: true"`
	Sha        string
	After      string
	Before     string
	Created    bool
	Deleted    bool
	Forced     bool
	HeadCommit *Commit `sql:"name: head"`
}

type Commit struct {
	ID        string
	Message   string
	Timestamp string
	Author    *Author
	Committer *Author
}

type Author struct {
	Name     string
	Email    string
	Username string
}


================================================
FILE: demo/hook_sql.go
================================================
package demo

// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.

import (
	"database/sql"
)

func ScanHook(row *sql.Row) (*Hook, error) {
	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 bool
	var v6 bool
	var v7 string
	var v8 string
	var v9 string
	var v10 string
	var v11 string
	var v12 string
	var v13 string
	var v14 string
	var v15 string

	err := row.Scan(
		&v0,
		&v1,
		&v2,
		&v3,
		&v4,
		&v5,
		&v6,
		&v7,
		&v8,
		&v9,
		&v10,
		&v11,
		&v12,
		&v13,
		&v14,
		&v15,
	)
	if err != nil {
		return nil, err
	}

	v := &Hook{}
	v.ID = v0
	v.Sha = v1
	v.After = v2
	v.Before = v3
	v.Created = v4
	v.Deleted = v5
	v.Forced = v6
	v.HeadCommit = &Commit{}
	v.HeadCommit.ID = v7
	v.HeadCommit.Message = v8
	v.HeadCommit.Timestamp = v9
	v.HeadCommit.Author = &Author{}
	v.HeadCommit.Author.Name = v10
	v.HeadCommit.Author.Email = v11
	v.HeadCommit.Author.Username = v12
	v.HeadCommit.Committer = &Author{}
	v.HeadCommit.Committer.Name = v13
	v.HeadCommit.Committer.Email = v14
	v.HeadCommit.Committer.Username = v15

	return v, nil
}

func ScanHooks(rows *sql.Rows) ([]*Hook, error) {
	var err error
	var vv []*Hook

	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 bool
	var v6 bool
	var v7 string
	var v8 string
	var v9 string
	var v10 string
	var v11 string
	var v12 string
	var v13 string
	var v14 string
	var v15 string

	for rows.Next() {
		err = rows.Scan(
			&v0,
			&v1,
			&v2,
			&v3,
			&v4,
			&v5,
			&v6,
			&v7,
			&v8,
			&v9,
			&v10,
			&v11,
			&v12,
			&v13,
			&v14,
			&v15,
		)
		if err != nil {
			return vv, err
		}

		v := &Hook{}
		v.ID = v0
		v.Sha = v1
		v.After = v2
		v.Before = v3
		v.Created = v4
		v.Deleted = v5
		v.Forced = v6
		v.HeadCommit = &Commit{}
		v.HeadCommit.ID = v7
		v.HeadCommit.Message = v8
		v.HeadCommit.Timestamp = v9
		v.HeadCommit.Author = &Author{}
		v.HeadCommit.Author.Name = v10
		v.HeadCommit.Author.Email = v11
		v.HeadCommit.Author.Username = v12
		v.HeadCommit.Committer = &Author{}
		v.HeadCommit.Committer.Name = v13
		v.HeadCommit.Committer.Email = v14
		v.HeadCommit.Committer.Username = v15

		vv = append(vv, v)
	}
	return vv, rows.Err()
}

func SliceHook(v *Hook) []interface{} {
	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 bool
	var v6 bool
	var v7 string
	var v8 string
	var v9 string
	var v10 string
	var v11 string
	var v12 string
	var v13 string
	var v14 string
	var v15 string

	v0 = v.ID
	v1 = v.Sha
	v2 = v.After
	v3 = v.Before
	v4 = v.Created
	v5 = v.Deleted
	v6 = v.Forced
	if v.HeadCommit != nil {
		v7 = v.HeadCommit.ID
		v8 = v.HeadCommit.Message
		v9 = v.HeadCommit.Timestamp
		if v.HeadCommit.Author != nil {
			v10 = v.HeadCommit.Author.Name
			v11 = v.HeadCommit.Author.Email
			v12 = v.HeadCommit.Author.Username
		}

	}

	if v.HeadCommit.Committer != nil {
		v13 = v.HeadCommit.Committer.Name
		v14 = v.HeadCommit.Committer.Email
		v15 = v.HeadCommit.Committer.Username
	}

	return []interface{}{
		v0,
		v1,
		v2,
		v3,
		v4,
		v5,
		v6,
		v7,
		v8,
		v9,
		v10,
		v11,
		v12,
		v13,
		v14,
		v15,
	}
}

func SelectHook(db *sql.DB, query string, args ...interface{}) (*Hook, error) {
	row := db.QueryRow(query, args...)
	return ScanHook(row)
}

func SelectHooks(db *sql.DB, query string, args ...interface{}) ([]*Hook, error) {
	rows, err := db.Query(query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return ScanHooks(rows)
}

func InsertHook(db *sql.DB, query string, v *Hook) error {

	res, err := db.Exec(query, SliceHook(v)[1:]...)
	if err != nil {
		return err
	}

	v.ID, err = res.LastInsertId()
	return err
}

func UpdateHook(db *sql.DB, query string, v *Hook) error {

	args := SliceHook(v)[1:]
	args = append(args, v.ID)
	_, err := db.Exec(query, args...)
	return err
}

const CreateHookStmt = `
CREATE TABLE IF NOT EXISTS hooks (
 hook_id                      INTEGER PRIMARY KEY AUTO_INCREMENT
,hook_sha                     VARCHAR(512)
,hook_after                   VARCHAR(512)
,hook_before                  VARCHAR(512)
,hook_created                 BOOLEAN
,hook_deleted                 BOOLEAN
,hook_forced                  BOOLEAN
,hook_head_id                 VARCHAR(512)
,hook_head_message            VARCHAR(512)
,hook_head_timestamp          VARCHAR(512)
,hook_head_author_name        VARCHAR(512)
,hook_head_author_email       VARCHAR(512)
,hook_head_author_username    VARCHAR(512)
,hook_head_committer_name     VARCHAR(512)
,hook_head_committer_email    VARCHAR(512)
,hook_head_committer_username VARCHAR(512)
);
`

const InsertHookStmt = `
INSERT INTO hooks (
 hook_sha
,hook_after
,hook_before
,hook_created
,hook_deleted
,hook_forced
,hook_head_id
,hook_head_message
,hook_head_timestamp
,hook_head_author_name
,hook_head_author_email
,hook_head_author_username
,hook_head_committer_name
,hook_head_committer_email
,hook_head_committer_username
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
`

const SelectHookStmt = `
SELECT 
 hook_id
,hook_sha
,hook_after
,hook_before
,hook_created
,hook_deleted
,hook_forced
,hook_head_id
,hook_head_message
,hook_head_timestamp
,hook_head_author_name
,hook_head_author_email
,hook_head_author_username
,hook_head_committer_name
,hook_head_committer_email
,hook_head_committer_username
FROM hooks 
`

const SelectHookRangeStmt = `
SELECT 
 hook_id
,hook_sha
,hook_after
,hook_before
,hook_created
,hook_deleted
,hook_forced
,hook_head_id
,hook_head_message
,hook_head_timestamp
,hook_head_author_name
,hook_head_author_email
,hook_head_author_username
,hook_head_committer_name
,hook_head_committer_email
,hook_head_committer_username
FROM hooks 
LIMIT ? OFFSET ?
`

const SelectHookCountStmt = `
SELECT count(1)
FROM hooks 
`

const SelectHookPkeyStmt = `
SELECT 
 hook_id
,hook_sha
,hook_after
,hook_before
,hook_created
,hook_deleted
,hook_forced
,hook_head_id
,hook_head_message
,hook_head_timestamp
,hook_head_author_name
,hook_head_author_email
,hook_head_author_username
,hook_head_committer_name
,hook_head_committer_email
,hook_head_committer_username
FROM hooks 
WHERE hook_id=?
`

const UpdateHookPkeyStmt = `
UPDATE hooks SET 
 hook_id=?
,hook_sha=?
,hook_after=?
,hook_before=?
,hook_created=?
,hook_deleted=?
,hook_forced=?
,hook_head_id=?
,hook_head_message=?
,hook_head_timestamp=?
,hook_head_author_name=?
,hook_head_author_email=?
,hook_head_author_username=?
,hook_head_committer_name=?
,hook_head_committer_email=?
,hook_head_committer_username=? 
WHERE hook_id=?
`

const DeleteHookPkeyStmt = `
DELETE FROM hooks 
WHERE hook_id=?
`


================================================
FILE: demo/issue.go
================================================
package demo

//go:generate ../sqlgen -file issue.go -type Issue -pkg demo -o issue_sql.go -db postgres

type Issue struct {
	ID       int64 `sql:"pk: true, auto: true"`
	Number   int
	Title    string   `sql:"size: 512"`
	Body     string   `sql:"size: 2048"`
	Assignee string   `sql:"index: issue_assignee"`
	State    string   `sql:"size: 50"`
	Labels   []string `sql:"encode: json"`

	locked bool `sql:"-"`
}


================================================
FILE: demo/issue_sql.go
================================================
package demo

// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.

import (
	"database/sql"
	"encoding/json"
)

func ScanIssue(row *sql.Row) (*Issue, error) {
	var v0 int64
	var v1 int
	var v2 string
	var v3 string
	var v4 string
	var v5 string
	var v6 []byte

	err := row.Scan(
		&v0,
		&v1,
		&v2,
		&v3,
		&v4,
		&v5,
		&v6,
	)
	if err != nil {
		return nil, err
	}

	v := &Issue{}
	v.ID = v0
	v.Number = v1
	v.Title = v2
	v.Body = v3
	v.Assignee = v4
	v.State = v5
	json.Unmarshal(v6, &v.Labels)

	return v, nil
}

func ScanIssues(rows *sql.Rows) ([]*Issue, error) {
	var err error
	var vv []*Issue

	var v0 int64
	var v1 int
	var v2 string
	var v3 string
	var v4 string
	var v5 string
	var v6 []byte

	for rows.Next() {
		err = rows.Scan(
			&v0,
			&v1,
			&v2,
			&v3,
			&v4,
			&v5,
			&v6,
		)
		if err != nil {
			return vv, err
		}

		v := &Issue{}
		v.ID = v0
		v.Number = v1
		v.Title = v2
		v.Body = v3
		v.Assignee = v4
		v.State = v5
		json.Unmarshal(v6, &v.Labels)

		vv = append(vv, v)
	}
	return vv, rows.Err()
}

func SliceIssue(v *Issue) []interface{} {
	var v0 int64
	var v1 int
	var v2 string
	var v3 string
	var v4 string
	var v5 string
	var v6 []byte

	v0 = v.ID
	v1 = v.Number
	v2 = v.Title
	v3 = v.Body
	v4 = v.Assignee
	v5 = v.State
	v6, _ = json.Marshal(&v.Labels)

	return []interface{}{
		v0,
		v1,
		v2,
		v3,
		v4,
		v5,
		v6,
	}
}

func SelectIssue(db *sql.DB, query string, args ...interface{}) (*Issue, error) {
	row := db.QueryRow(query, args...)
	return ScanIssue(row)
}

func SelectIssues(db *sql.DB, query string, args ...interface{}) ([]*Issue, error) {
	rows, err := db.Query(query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return ScanIssues(rows)
}

func InsertIssue(db *sql.DB, query string, v *Issue) error {

	res, err := db.Exec(query, SliceIssue(v)[1:]...)
	if err != nil {
		return err
	}

	v.ID, err = res.LastInsertId()
	return err
}

func UpdateIssue(db *sql.DB, query string, v *Issue) error {

	args := SliceIssue(v)[1:]
	args = append(args, v.ID)
	_, err := db.Exec(query, args...)
	return err
}

const CreateIssueStmt = `
CREATE TABLE IF NOT EXISTS issues (
 issue_id       SERIAL PRIMARY KEY 
,issue_number   INTEGER
,issue_title    VARCHAR(512)
,issue_body     VARCHAR(2048)
,issue_assignee VARCHAR(512)
,issue_state    VARCHAR(50)
,issue_labels   BYTEA
);
`

const InsertIssueStmt = `
INSERT INTO issues (
 issue_number
,issue_title
,issue_body
,issue_assignee
,issue_state
,issue_labels
) VALUES ($1,$2,$3,$4,$5,$6)
`

const SelectIssueStmt = `
SELECT 
 issue_id
,issue_number
,issue_title
,issue_body
,issue_assignee
,issue_state
,issue_labels
FROM issues 
`

const SelectIssueRangeStmt = `
SELECT 
 issue_id
,issue_number
,issue_title
,issue_body
,issue_assignee
,issue_state
,issue_labels
FROM issues 
LIMIT $1 OFFSET $2
`

const SelectIssueCountStmt = `
SELECT count(1)
FROM issues 
`

const SelectIssuePkeyStmt = `
SELECT 
 issue_id
,issue_number
,issue_title
,issue_body
,issue_assignee
,issue_state
,issue_labels
FROM issues 
WHERE issue_id=$1
`

const UpdateIssuePkeyStmt = `
UPDATE issues SET 
 issue_id=$1
,issue_number=$2
,issue_title=$3
,issue_body=$4
,issue_assignee=$5
,issue_state=$6
,issue_labels=$7 
WHERE issue_id=$8
`

const DeleteIssuePkeyStmt = `
DELETE FROM issues 
WHERE issue_id=$1
`


================================================
FILE: demo/user.go
================================================
package demo

//go:generate ../sqlgen -file user.go -type User -pkg demo -o user_sql.go

type User struct {
	ID     int64  `sql:"pk: true, auto: true"`
	Login  string `sql:"unique: user_login"`
	Email  string `sql:"unique: user_email"`
	Avatar string
	Active bool
	Admin  bool

	// oauth token and secret
	token  string
	secret string

	// randomly generated hash used to sign user
	// session and application tokens.
	hash string
}


================================================
FILE: demo/user_sql.go
================================================
package demo

// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.

import (
	"database/sql"
)

func ScanUser(row *sql.Row) (*User, error) {
	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 bool
	var v6 string
	var v7 string
	var v8 string

	err := row.Scan(
		&v0,
		&v1,
		&v2,
		&v3,
		&v4,
		&v5,
		&v6,
		&v7,
		&v8,
	)
	if err != nil {
		return nil, err
	}

	v := &User{}
	v.ID = v0
	v.Login = v1
	v.Email = v2
	v.Avatar = v3
	v.Active = v4
	v.Admin = v5
	v.token = v6
	v.secret = v7
	v.hash = v8

	return v, nil
}

func ScanUsers(rows *sql.Rows) ([]*User, error) {
	var err error
	var vv []*User

	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 bool
	var v6 string
	var v7 string
	var v8 string

	for rows.Next() {
		err = rows.Scan(
			&v0,
			&v1,
			&v2,
			&v3,
			&v4,
			&v5,
			&v6,
			&v7,
			&v8,
		)
		if err != nil {
			return vv, err
		}

		v := &User{}
		v.ID = v0
		v.Login = v1
		v.Email = v2
		v.Avatar = v3
		v.Active = v4
		v.Admin = v5
		v.token = v6
		v.secret = v7
		v.hash = v8

		vv = append(vv, v)
	}
	return vv, rows.Err()
}

func SliceUser(v *User) []interface{} {
	var v0 int64
	var v1 string
	var v2 string
	var v3 string
	var v4 bool
	var v5 bool
	var v6 string
	var v7 string
	var v8 string

	v0 = v.ID
	v1 = v.Login
	v2 = v.Email
	v3 = v.Avatar
	v4 = v.Active
	v5 = v.Admin
	v6 = v.token
	v7 = v.secret
	v8 = v.hash

	return []interface{}{
		v0,
		v1,
		v2,
		v3,
		v4,
		v5,
		v6,
		v7,
		v8,
	}
}

func SelectUser(db *sql.DB, query string, args ...interface{}) (*User, error) {
	row := db.QueryRow(query, args...)
	return ScanUser(row)
}

func SelectUsers(db *sql.DB, query string, args ...interface{}) ([]*User, error) {
	rows, err := db.Query(query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return ScanUsers(rows)
}

func InsertUser(db *sql.DB, query string, v *User) error {

	res, err := db.Exec(query, SliceUser(v)[1:]...)
	if err != nil {
		return err
	}

	v.ID, err = res.LastInsertId()
	return err
}

func UpdateUser(db *sql.DB, query string, v *User) error {

	args := SliceUser(v)[1:]
	args = append(args, v.ID)
	_, err := db.Exec(query, args...)
	return err
}

const CreateUserStmt = `
CREATE TABLE IF NOT EXISTS users (
 user_id     INTEGER PRIMARY KEY AUTOINCREMENT
,user_login  TEXT
,user_email  TEXT
,user_avatar TEXT
,user_active BOOLEAN
,user_admin  BOOLEAN
,user_token  TEXT
,user_secret TEXT
,user_hash   TEXT
);
`

const InsertUserStmt = `
INSERT INTO users (
 user_login
,user_email
,user_avatar
,user_active
,user_admin
,user_token
,user_secret
,user_hash
) VALUES (?,?,?,?,?,?,?,?)
`

const SelectUserStmt = `
SELECT 
 user_id
,user_login
,user_email
,user_avatar
,user_active
,user_admin
,user_token
,user_secret
,user_hash
FROM users 
`

const SelectUserRangeStmt = `
SELECT 
 user_id
,user_login
,user_email
,user_avatar
,user_active
,user_admin
,user_token
,user_secret
,user_hash
FROM users 
LIMIT ? OFFSET ?
`

const SelectUserCountStmt = `
SELECT count(1)
FROM users 
`

const SelectUserPkeyStmt = `
SELECT 
 user_id
,user_login
,user_email
,user_avatar
,user_active
,user_admin
,user_token
,user_secret
,user_hash
FROM users 
WHERE user_id=?
`

const UpdateUserPkeyStmt = `
UPDATE users SET 
 user_id=?
,user_login=?
,user_email=?
,user_avatar=?
,user_active=?
,user_admin=?
,user_token=?
,user_secret=?
,user_hash=? 
WHERE user_id=?
`

const DeleteUserPkeyStmt = `
DELETE FROM users 
WHERE user_id=?
`

const CreateUserLoginStmt = `
CREATE UNIQUE INDEX IF NOT EXISTS user_login ON users (user_login)
`

const SelectUserLoginStmt = `
SELECT 
 user_id
,user_login
,user_email
,user_avatar
,user_active
,user_admin
,user_token
,user_secret
,user_hash
FROM users 
WHERE user_login=?
`

const UpdateUserLoginStmt = `
UPDATE users SET 
 user_id=?
,user_login=?
,user_email=?
,user_avatar=?
,user_active=?
,user_admin=?
,user_token=?
,user_secret=?
,user_hash=? 
WHERE user_login=?
`

const DeleteUserLoginStmt = `
DELETE FROM users 
WHERE user_login=?
`

const CreateUserEmailStmt = `
CREATE UNIQUE INDEX IF NOT EXISTS user_email ON users (user_email)
`

const SelectUserEmailStmt = `
SELECT 
 user_id
,user_login
,user_email
,user_avatar
,user_active
,user_admin
,user_token
,user_secret
,user_hash
FROM users 
WHERE user_email=?
`

const UpdateUserEmailStmt = `
UPDATE users SET 
 user_id=?
,user_login=?
,user_email=?
,user_avatar=?
,user_active=?
,user_admin=?
,user_token=?
,user_secret=?
,user_hash=? 
WHERE user_email=?
`

const DeleteUserEmailStmt = `
DELETE FROM users 
WHERE user_email=?
`


================================================
FILE: fmt.go
================================================
package main

import (
	"bytes"
	"io"
	"os"
	"os/exec"
)

// format formats a template using gofmt.
func format(in io.Reader) (io.Reader, error) {
	var out bytes.Buffer

	gofmt := exec.Command("gofmt", "-s")
	gofmt.Stdin = in
	gofmt.Stdout = &out
	gofmt.Stderr = os.Stderr
	err := gofmt.Run()
	return &out, err
}


================================================
FILE: gen.go
================================================
package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"os"

	"github.com/drone/sqlgen/parse"
	"github.com/drone/sqlgen/schema"
)

var (
	input      = flag.String("file", "", "input file name; required")
	output     = flag.String("o", "", "output file name; required")
	pkgName    = flag.String("pkg", "main", "output package name; required")
	typeName   = flag.String("type", "", "type to generate; required")
	database   = flag.String("db", "sqlite", "sql dialect; required")
	genSchema  = flag.Bool("schema", true, "generate sql schema and queries")
	genFuncs   = flag.Bool("funcs", true, "generate sql helper functions")
	extraFuncs = flag.Bool("extras", true, "generate extra sql helper functions")
)

func main() {
	flag.Parse()

	// parses the syntax tree into something a bit
	// easier to work with.
	tree, err := parse.Parse(*input, *typeName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		os.Exit(1)
	}

	// if the code is generated in a different folder
	// that the struct we need to import the struct
	if tree.Pkg != *pkgName && *pkgName != "main" {
		// TODO
	}

	// load the Tree into a schema Object
	table := schema.Load(tree)
	dialect := schema.New(schema.Dialects[*database])

	var buf bytes.Buffer

	if *genFuncs {
		writePackage(&buf, *pkgName)
		writeImports(&buf, tree, "database/sql")
		writeRowFunc(&buf, tree)
		writeRowsFunc(&buf, tree)
		writeSliceFunc(&buf, tree)

		if *extraFuncs {
			writeSelectRow(&buf, tree)
			writeSelectRows(&buf, tree)
			writeInsertFunc(&buf, tree)
			writeUpdateFunc(&buf, tree)
		}
	} else {
		writePackage(&buf, *pkgName)
	}

	// write the sql functions
	if *genSchema {
		writeSchema(&buf, dialect, table)
	}

	// formats the generated file using gofmt
	pretty, err := format(&buf)
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err)
		return
	}

	// create output source for file. defaults to
	// stdout but may be file.
	var out io.WriteCloser = os.Stdout
	if *output != "" {
		out, err = os.Create(*output)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%v\n", err)
			return
		}
		defer out.Close()
	}

	io.Copy(out, pretty)
}


================================================
FILE: gen_funcs.go
================================================
package main

import (
	"bytes"
	"fmt"
	"io"
	"strings"

	"github.com/acsellers/inflections"
	"github.com/drone/sqlgen/parse"
)

func writeImports(w io.Writer, tree *parse.Node, pkgs ...string) {
	var pmap = map[string]struct{}{}

	// add default packages
	for _, pkg := range pkgs {
		pmap[pkg] = struct{}{}
	}

	// check each edge node to see if it is
	// encoded, which might require us to import
	// other packages
	for _, node := range tree.Edges() {
		if node.Tags == nil || len(node.Tags.Encode) == 0 {
			continue
		}
		switch node.Tags.Encode {
		case "json":
			pmap["encoding/json"] = struct{}{}
			// case "gzip":
			// 	pmap["compress/gzip"] = struct{}{}
			// case "snappy":
			// 	pmap["github.com/golang/snappy"] = struct{}{}
		}
	}

	if len(pmap) == 0 {
		return
	}

	// write the import block, including each
	// encoder package that was specified.
	fmt.Fprintln(w, "\nimport (")
	for pkg, _ := range pmap {
		fmt.Fprintf(w, "\t%q\n", pkg)
	}
	fmt.Fprintln(w, ")")
}

func writeSliceFunc(w io.Writer, tree *parse.Node) {

	var buf1, buf2, buf3 bytes.Buffer

	var i, depth int
	var parent = tree

	for _, node := range tree.Edges() {
		if node.Tags.Skip {
			continue
		}

		// temporary variable declaration
		switch node.Kind {
		case parse.Map, parse.Slice:
			fmt.Fprintf(&buf1, "var v%d %s\n", i, "[]byte")
		default:
			fmt.Fprintf(&buf1, "var v%d %s\n", i, node.Type)
		}

		// variable scanning
		fmt.Fprintf(&buf3, "v%d,\n", i)

		// variable setting
		path := node.Path()[1:]

		// if the parent is a ptr struct we
		// need to create a new
		if parent != node.Parent && node.Parent.Kind == parse.Ptr {
			// if node.Parent != nil && node.Parent.Parent != parent {
			// 	fmt.Fprintln(&buf2, "}\n")
			// 	depth--
			// }

			// seriously ... this works?
			if node.Parent != nil && node.Parent.Parent != parent {
				for _, p := range path {
					if p == parent || depth == 0 {
						break
					}
					fmt.Fprintln(&buf2, "}\n")
					depth--
				}
			}
			depth++
			fmt.Fprintf(&buf2, "if v.%s != nil {\n", join(path[:len(path)-1], "."))
		}

		switch node.Kind {
		case parse.Map, parse.Slice, parse.Struct, parse.Ptr:
			fmt.Fprintf(&buf2, "v%d, _ = json.Marshal(&v.%s)\n", i, join(path, "."))
		default:
			fmt.Fprintf(&buf2, "v%d=v.%s\n", i, join(path, "."))
		}

		parent = node.Parent
		i++
	}

	for depth != 0 {
		depth--
		fmt.Fprintln(&buf2, "}\n")
	}

	fmt.Fprintf(w,
		sSliceRow,
		tree.Type,
		tree.Type,
		buf1.String(),
		buf2.String(),
		buf3.String(),
	)
}

func writeRowFunc(w io.Writer, tree *parse.Node) {

	var buf1, buf2, buf3 bytes.Buffer

	var i int
	var parent = tree
	for _, node := range tree.Edges() {
		if node.Tags.Skip {
			continue
		}

		// temporary variable declaration
		switch node.Kind {
		case parse.Map, parse.Slice:
			fmt.Fprintf(&buf1, "var v%d %s\n", i, "[]byte")
		default:
			fmt.Fprintf(&buf1, "var v%d %s\n", i, node.Type)
		}

		// variable scanning
		fmt.Fprintf(&buf2, "&v%d,\n", i)

		// variable setting
		path := node.Path()[1:]

		// if the parent is a ptr struct we
		// need to create a new
		if parent != node.Parent && node.Parent.Kind == parse.Ptr {
			fmt.Fprintf(&buf3, "v.%s=&%s{}\n", join(path[:len(path)-1], "."), node.Parent.Type)
		}

		switch node.Kind {
		case parse.Map, parse.Slice, parse.Struct, parse.Ptr:
			fmt.Fprintf(&buf3, "json.Unmarshal(v%d, &v.%s)\n", i, join(path, "."))
		default:
			fmt.Fprintf(&buf3, "v.%s=v%d\n", join(path, "."), i)
		}

		parent = node.Parent
		i++
	}

	fmt.Fprintf(w,
		sScanRow,
		tree.Type,
		tree.Type,
		buf1.String(),
		buf2.String(),
		tree.Type,
		buf3.String(),
	)
}

func writeRowsFunc(w io.Writer, tree *parse.Node) {
	var buf1, buf2, buf3 bytes.Buffer

	var i int
	var parent = tree
	for _, node := range tree.Edges() {
		if node.Tags.Skip {
			continue
		}

		// temporary variable declaration
		switch node.Kind {
		case parse.Map, parse.Slice:
			fmt.Fprintf(&buf1, "var v%d %s\n", i, "[]byte")
		default:
			fmt.Fprintf(&buf1, "var v%d %s\n", i, node.Type)
		}

		// variable scanning
		fmt.Fprintf(&buf2, "&v%d,\n", i)

		// variable setting
		path := node.Path()[1:]

		// if the parent is a ptr struct we
		// need to create a new
		if parent != node.Parent && node.Parent.Kind == parse.Ptr {
			fmt.Fprintf(&buf3, "v.%s=&%s{}\n", join(path[:len(path)-1], "."), node.Parent.Type)
		}

		switch node.Kind {
		case parse.Map, parse.Slice, parse.Struct, parse.Ptr:
			fmt.Fprintf(&buf3, "json.Unmarshal(v%d, &v.%s)\n", i, join(path, "."))
		default:
			fmt.Fprintf(&buf3, "v.%s=v%d\n", join(path, "."), i)
		}

		parent = node.Parent
		i++
	}

	fmt.Fprintf(w,
		sScanRows,
		inflections.Pluralize(tree.Type),
		tree.Type,
		tree.Type,
		buf1.String(),
		buf2.String(),
		tree.Type,
		buf3.String(),
	)
}

func writeSelectRow(w io.Writer, tree *parse.Node) {
	fmt.Fprintf(w, sSelectRow, tree.Type, tree.Type, tree.Type)
}

func writeSelectRows(w io.Writer, tree *parse.Node) {
	plural := inflections.Pluralize(tree.Type)
	fmt.Fprintf(w, sSelectRows, plural, tree.Type, plural)
}

func writeInsertFunc(w io.Writer, tree *parse.Node) {
	// TODO this assumes I'm using the ID field.
	// we should not make that assumption
	fmt.Fprintf(w, sInsert, tree.Type, tree.Type, tree.Type)
}

func writeUpdateFunc(w io.Writer, tree *parse.Node) {
	fmt.Fprintf(w, sUpdate, tree.Type, tree.Type, tree.Type)
}

// join is a helper function that joins nodes
// together by name using the seperator.
func join(nodes []*parse.Node, sep string) string {
	var parts []string
	for _, node := range nodes {
		parts = append(parts, node.Name)
	}
	return strings.Join(parts, sep)
}


================================================
FILE: gen_schema.go
================================================
package main

import (
	"fmt"
	"io"
	"strings"

	"bitbucket.org/pkg/inflect"
	"github.com/drone/sqlgen/schema"
)

// writeSchema writes SQL statements to CREATE, INSERT,
// UPDATE and DELETE values from Table t.
func writeSchema(w io.Writer, d schema.Dialect, t *schema.Table) {

	writeConst(w,
		d.Table(t),
		"create", inflect.Singularize(t.Name), "stmt",
	)

	writeConst(w,
		d.Insert(t),
		"insert", inflect.Singularize(t.Name), "stmt",
	)

	writeConst(w,
		d.Select(t, nil),
		"select", inflect.Singularize(t.Name), "stmt",
	)

	writeConst(w,
		d.SelectRange(t, nil),
		"select", inflect.Singularize(t.Name), "range", "stmt",
	)

	writeConst(w,
		d.SelectCount(t, nil),
		"select", inflect.Singularize(t.Name), "count", "stmt",
	)

	if len(t.Primary) != 0 {
		writeConst(w,
			d.Select(t, t.Primary),
			"select", inflect.Singularize(t.Name), "pkey", "stmt",
		)

		writeConst(w,
			d.Update(t, t.Primary),
			"update", inflect.Singularize(t.Name), "pkey", "stmt",
		)

		writeConst(w,
			d.Delete(t, t.Primary),
			"delete", inflect.Singularize(t.Name), "pkey", "stmt",
		)
	}

	for _, ix := range t.Index {

		writeConst(w,
			d.Index(t, ix),
			"create", ix.Name, "stmt",
		)

		writeConst(w,
			d.Select(t, ix.Fields),
			"select", ix.Name, "stmt",
		)

		if !ix.Unique {

			writeConst(w,
				d.SelectRange(t, ix.Fields),
				"select", ix.Name, "range", "stmt",
			)

			writeConst(w,
				d.SelectCount(t, ix.Fields),
				"select", ix.Name, "count", "stmt",
			)

		} else {

			writeConst(w,
				d.Update(t, ix.Fields),
				"update", ix.Name, "stmt",
			)

			writeConst(w,
				d.Delete(t, ix.Fields),
				"delete", ix.Name, "stmt",
			)
		}
	}
}

// WritePackage writes the Go package header to
// writer w with the given package name.
func writePackage(w io.Writer, name string) {
	fmt.Fprintf(w, sPackage, name)
}

// writeConst is a helper function that writes the
// body string to a Go const variable.
func writeConst(w io.Writer, body string, label ...string) {
	// create a snake case variable name from
	// the specified labels. Then convert the
	// variable name to a quoted, camel case string.
	name := strings.Join(label, "_")
	name = inflect.Typeify(name)

	// quote the body using multi-line quotes
	body = fmt.Sprintf(sQuote, body)

	fmt.Fprintf(w, sConst, name, body)
}


================================================
FILE: parse/const.go
================================================
package parse

const (
	Invalid = iota
	Bool
	Int
	Int8
	Int16
	Int32
	Int64
	Uint
	Uint8
	Uint16
	Uint32
	Uint64
	Float32
	Float64
	Complex64
	Complex128
	Interface
	Bytes
	Map
	Ptr
	String
	Slice
	Struct
)

var Types = map[string]uint8{
	"bool":        Bool,
	"int":         Int,
	"int8":        Int8,
	"int16":       Int16,
	"int32":       Int32,
	"int64":       Int64,
	"uint":        Uint,
	"uint8":       Uint8,
	"uint16":      Uint16,
	"uint32":      Uint32,
	"uint64":      Uint64,
	"float32":     Float32,
	"float64":     Float64,
	"complex64":   Complex64,
	"complex128":  Complex128,
	"interface{}": Interface,
	"[]byte":      Bytes,
	"string":      String,
}


================================================
FILE: parse/node.go
================================================
package parse

type Node struct {
	Pkg  string // source code package.
	Name string // source code name.
	Kind uint8  // source code kind.
	Type string // source code type.
	Tags *Tag

	Parent *Node
	Nodes  []*Node
}

func (n *Node) append(node *Node) {
	node.Parent = n
	n.Nodes = append(n.Nodes, node)
}

// Walk traverses the node tree, invoking the callback
// function for each node that is traversed.
func (n *Node) Walk(fn func(*Node)) {
	for _, node := range n.Nodes {
		fn(node)
		node.Walk(fn)
	}
}

// WalkRev traverses the tree in reverse order, invoking
// the callback function for each parent node until
// the root node is reached.
func (n *Node) WalkRev(fn func(*Node)) {
	if n.Parent != nil {
		n.Parent.WalkRev(fn)
	}
	fn(n) // this was previously inside the if block
}

// Edges returns a flattened list of all edge
// nodes in the Tree.
func (n *Node) Edges() []*Node {
	var nodes []*Node
	n.Walk(func(node *Node) {
		if len(node.Nodes) == 0 {
			nodes = append(nodes, node)
		}
	})
	return nodes
}

// Path returns the absolute path of the node
// in the Tree.
func (n *Node) Path() []*Node {
	var nodes []*Node
	n.WalkRev(func(node *Node) {
		nodes = append(nodes, node)
	})
	return nodes
}


================================================
FILE: parse/parse.go
================================================
package parse

import (
	"errors"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
)

var (
	ErrTypeNotFound = errors.New("Cannot find type in the source code.")
	ErrTypeInvalid  = errors.New("Cannot convert type to a SQL type.")
)

func Parse(path, name string) (*Node, error) {

	var fset = token.NewFileSet()
	var file, err = parser.ParseFile(fset, path, nil, parser.ParseComments)
	if err != nil {
		return nil, err
	}

	for _, decl := range file.Decls {
		gen, ok := decl.(*ast.GenDecl)
		if !ok {
			continue
		}
		spec, ok := gen.Specs[0].(*ast.TypeSpec)
		if !ok {
			continue
		}
		if spec.Name.String() != name {
			continue
		}

		var node = new(Node)
		node.Name = spec.Name.String()
		node.Type = spec.Name.String()
		node.Pkg = file.Name.Name
		err = buildNodes(node, spec)
		return node, err
	}

	return nil, ErrTypeNotFound
}

func buildNodes(parent *Node, spec *ast.TypeSpec) error {
	ident, ok := spec.Type.(*ast.StructType)
	if !ok {
		return ErrTypeInvalid
	}

	for _, field := range ident.Fields.List {
		var tag string
		if field.Tag != nil {
			tag = field.Tag.Value
		}
		buildNode(parent, field.Type, field.Names[0].Name, tag)
	}
	return nil
}

func buildNode(parent *Node, expr ast.Expr, name, tag string) error {
	var err error

	switch ident := expr.(type) {
	case *ast.Ident:
		if ident.Obj == nil {
			node := &Node{
				Name: name,
				Type: ident.Name,
				Kind: Types[ident.Name],
			}
			node.Tags, err = parseTag(tag)
			if err != nil {
				return err
			}
			parent.append(node)
			return nil
		}
		spec, ok := ident.Obj.Decl.(*ast.TypeSpec)
		if !ok {
			goto invalidType
		}
		node := &Node{
			Name: name,
			Type: ident.Name,
			Kind: Struct,
		}
		node.Tags, err = parseTag(tag)
		if err != nil {
			return err
		}
		parent.append(node)
		return buildNodes(node, spec)

	case *ast.ArrayType:
		if ident.Len != nil {
			goto invalidType
		}
		node := &Node{
			Name: name,
			Kind: Slice,
			Type: fmt.Sprintf("[]%s", ident.Elt),
		}
		node.Tags, err = parseTag(tag)
		if err != nil {
			return err
		}
		if node.Type == "[]byte" {
			node.Kind = Bytes
		}
		parent.append(node)
		return nil

	case *ast.MapType:
		type_ := fmt.Sprintf("map[%s]%s", ident.Key, ident.Value)
		node := &Node{Name: name, Type: type_, Kind: Map}
		node.Tags, err = parseTag(tag)
		if err != nil {
			return err
		}
		parent.append(node)
		return nil

	case *ast.StarExpr:
		innerIdent, ok := ident.X.(*ast.Ident)
		if !ok {
			goto invalidType
		}
		if innerIdent.Obj == nil || innerIdent.Obj.Decl == nil {
			goto invalidType
		}
		spec, ok := innerIdent.Obj.Decl.(*ast.TypeSpec)
		if !ok {
			goto invalidType
		}
		node := &Node{Name: name, Type: innerIdent.Name, Kind: Ptr}
		node.Tags, err = parseTag(tag)
		if err != nil {
			return err
		}
		if node.Tags.Skip {
			return nil
		}
		parent.append(node)
		return buildNodes(node, spec)
	}

invalidType:
	return fmt.Errorf("%s is not a valid type", name)
}


================================================
FILE: parse/tag.go
================================================
package parse

import (
	"fmt"
	"reflect"
	"strings"

	"gopkg.in/yaml.v2"
)

const (
	EncodeGzip = "gzip"
	EncodeJson = "json"
)

// Tag stores the parsed data from the tag string in
// a struct field.
type Tag struct {
	Name    string `yaml:"name"`
	Type    string `yaml:"type"`
	Primary bool   `yaml:"pk"`
	Auto    bool   `yaml:"auto"`
	Index   string `yaml:"index"`
	Unique  string `yaml:"unique"`
	Size    int    `yaml:"size"`
	Skip    bool   `yaml:"skip"`
	Encode  string `yaml:"encode"`
}

// parseTag parses a tag string from the struct
// field and unmarshals into a Tag struct.
func parseTag(raw string) (*Tag, error) {
	var tag = new(Tag)

	raw = strings.Replace(raw, "`", "", -1)
	raw = reflect.StructTag(raw).Get("sql")

	// if the tag indicates the field should
	// be skipped we can exit right away.
	if strings.TrimSpace(raw) == "-" {
		tag.Skip = true
		return tag, nil
	}

	// otherwise wrap the string in curly braces
	// so that we can use the Yaml parser.
	raw = fmt.Sprintf("{ %s }", raw)

	// unmarshals the Yaml formatted string into
	// the Tag structure.
	var err = yaml.Unmarshal([]byte(raw), tag)
	return tag, err
}


================================================
FILE: parse/tag_test.go
================================================
package parse

import (
	"reflect"
	"testing"
)

var tagTests = []struct {
	raw string
	tag *Tag
}{
	{
		`sql:"-"`,
		&Tag{Skip: true},
	},
	{
		`sql:"pk: true, auto: true"`,
		&Tag{Primary: true, Auto: true},
	},
	{
		`sql:"name: foo"`,
		&Tag{Name: "foo"},
	},
	{
		`sql:"type: varchar"`,
		&Tag{Type: "varchar"},
	},
	{
		`sql:"size: 2048"`,
		&Tag{Size: 2048},
	},
	{
		`sql:"index: fake_index"`,
		&Tag{Index: "fake_index"},
	},
	{
		`sql:"unique: fake_unique_index"`,
		&Tag{Unique: "fake_unique_index"},
	},
}

func TestParseTag(t *testing.T) {
	for _, test := range tagTests {

		var want = test.tag
		var got, err = parseTag(test.raw)

		if err != nil {
			t.Errorf("Got Error parsing Tag %s. %s", test.raw, err)
		}

		if !reflect.DeepEqual(got, want) {
			t.Errorf("Wanted Tag %+v, got Tag %+v", want, got)
		}
	}
}


================================================
FILE: schema/base.go
================================================
package schema

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"text/tabwriter"
)

type base struct {
	Dialect Dialect
}

// Table returns a SQL statement to create the table.
func (b *base) Table(t *Table) string {

	// use a large default buffer size of so that
	// the tabbing doesn't get prematurely flushed
	// resulting in un-even lines.
	var byt = make([]byte, 0, 100000)
	var buf = bytes.NewBuffer(byt)

	// use a tab writer to evenly space the column
	// names and column types.
	var tab = tabwriter.NewWriter(buf, 0, 8, 1, ' ', 0)
	b.columnw(tab, t.Fields, false, false, true)

	// flush the tab writer to write to the buffer
	tab.Flush()

	return fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (%s\n);", t.Name, buf.String())
}

// Index returns a SQL statement to create the index.
func (b *base) Index(table *Table, index *Index) string {
	var obj = "INDEX"
	if index.Unique {
		obj = "UNIQUE INDEX"
	}
	return fmt.Sprintf("CREATE %s IF NOT EXISTS %s ON %s (%s)", obj, index.Name, table.Name, b.columns(index.Fields, true, false, false))
}

func (b *base) Insert(t *Table) string {
	var fields []*Field
	var params []string
	var i int

	for _, field := range t.Fields {
		if !field.Auto {
			fields = append(fields, field)
			params = append(params, b.Dialect.Param(i))
			i++
		}
	}

	return fmt.Sprintf("INSERT INTO %s (%s\n) VALUES (%s)", t.Name, b.columns(fields, false, false, false), strings.Join(params, ","))
}

func (b *base) Update(t *Table, fields []*Field) string {
	return fmt.Sprintf("UPDATE %s SET %s %s", t.Name, b.columns(t.Fields, false, true, false), b.clause(fields, len(t.Fields)))
}

func (b *base) Delete(t *Table, fields []*Field) string {
	return fmt.Sprintf("DELETE FROM %s %s", t.Name, b.clause(fields, 0))
}

func (b *base) Select(t *Table, fields []*Field) string {
	return fmt.Sprintf("SELECT %s\nFROM %s %s", b.columns(t.Fields, false, false, false), t.Name, b.clause(fields, 0))
}

func (b *base) SelectRange(t *Table, fields []*Field) string {
	return fmt.Sprintf("SELECT %s\nFROM %s %s\nLIMIT %s OFFSET %s", b.columns(t.Fields, false, false, false), t.Name, b.clause(fields, 0), b.Dialect.Param(len(fields)), b.Dialect.Param(len(fields)+1))
}

func (b *base) SelectCount(t *Table, fields []*Field) string {
	return fmt.Sprintf("SELECT count(1)\nFROM %s %s", t.Name, b.clause(fields, 0))
}

// Param returns the parameters symbol used in prepared
// sql statements.
func (b *base) Param(i int) string {
	return "?"
}

// Column returns a SQL type for the given field.
//
// For Mysql and Postgres see:
// https://github.com/eaigner/hood/blob/master/mysql.go#L35
func (b *base) Column(f *Field) string {
	switch f.Type {
	case INTEGER:
		return "INTEGER"
	case BOOLEAN:
		return "BOOLEAN"
	case BLOB:
		return "BLOB"
	case VARCHAR:
		return "TEXT"
	default:
		return "TEXT"
	}
}

// Token returns the SQL string for the requested token.
func (b *base) Token(v int) (_ string) {
	switch v {
	case AUTO_INCREMENT:
		return "AUTOINCREMENT"
	case PRIMARY_KEY:
		return "PRIMARY KEY"
	default:
		return
	}
}

// helper function to generate a block of columns. You
// can optionally generate in inline list of columns,
// include an assignment operator, and include column
// definitions.
func (b *base) columns(fields []*Field, inline, assign, def bool) string {
	var buf bytes.Buffer
	b.columnw(&buf, fields, inline, assign, def)
	return buf.String()
}

// helper function to write a block of columns to w.
func (b *base) columnw(w io.Writer, fields []*Field, inline, assign, def bool) {

	for i, field := range fields {
		if !inline {
			io.WriteString(w, "\n")
		}

		switch {
		case i == 0 && !inline:
			io.WriteString(w, " ")
		case i != 0:
			io.WriteString(w, ",")
		}
		io.WriteString(w, field.Name)

		if assign {
			io.WriteString(w, "=")
			io.WriteString(w, b.Dialect.Param(i))
		}

		if !def {
			continue
		}

		io.WriteString(w, "\t")
		io.WriteString(w, b.Dialect.Column(field))

		if field.Primary {
			io.WriteString(w, " ")
			io.WriteString(w, b.Dialect.Token(PRIMARY_KEY))
		}

		if field.Auto {
			io.WriteString(w, " ")
			io.WriteString(w, b.Dialect.Token(AUTO_INCREMENT))
		}
	}
}

// helper function to generate the Where clause
// section of a SQL statement
func (b *base) clause(fields []*Field, pos int) string {
	var buf bytes.Buffer

	var i int
	for _, field := range fields {
		buf.WriteString("\n")
		switch {
		case i == 0:
			buf.WriteString("WHERE")
		default:
			buf.WriteString("AND")
		}

		buf.WriteString(" ")
		buf.WriteString(field.Name)
		buf.WriteString("=")
		buf.WriteString(b.Dialect.Param(i + pos))

		i++
	}
	return buf.String()
}


================================================
FILE: schema/dialect.go
================================================
package schema

const (
	SQLITE int = iota
	POSTGRES
	MYSQL
)

var Dialects = map[string]int{
	"sqlite":   SQLITE,
	"postgres": POSTGRES,
	"mysql":    MYSQL,
}

type Dialect interface {
	Table(*Table) string
	Index(*Table, *Index) string
	Column(*Field) string
	Insert(*Table) string
	Update(*Table, []*Field) string
	Delete(*Table, []*Field) string
	Select(*Table, []*Field) string
	SelectCount(*Table, []*Field) string
	SelectRange(*Table, []*Field) string
	Param(int) string
	Token(int) string
}

func New(dialect int) Dialect {
	switch dialect {
	case POSTGRES:
		return newPosgres()
	case MYSQL:
		return newMysql()
	default:
		return newSqlite()
	}
}


================================================
FILE: schema/dialect_mysql.go
================================================
package schema

import (
	"fmt"
)

type mysql struct {
	base
}

func newMysql() Dialect {
	d := &mysql{}
	d.base.Dialect = d
	return d
}

func (d *mysql) Column(f *Field) (_ string) {
	switch f.Type {
	case INTEGER:
		return "INTEGER"
	case BOOLEAN:
		return "BOOLEAN"
	case BLOB:
		return "MEDIUMBLOB"
	case VARCHAR:
		// assigns an arbitrary size if
		// none is provided.
		size := f.Size
		if size == 0 {
			size = 512
		}
		return fmt.Sprintf("VARCHAR(%d)", size)
	default:
		return
	}
}

func (d *mysql) Token(v int) (_ string) {
	switch v {
	case AUTO_INCREMENT:
		return "AUTO_INCREMENT"
	case PRIMARY_KEY:
		return "PRIMARY KEY"
	default:
		return
	}
}


================================================
FILE: schema/dialect_postgres.go
================================================
package schema

import (
	"fmt"
)

type posgres struct {
	base
}

func newPosgres() Dialect {
	d := &posgres{}
	d.base.Dialect = d
	return d
}

func (d *posgres) Column(f *Field) (_ string) {
	// posgres uses a special column type
	// to autoincrementing keys.
	if f.Auto {
		return "SERIAL"
	}

	switch f.Type {
	case INTEGER:
		return "INTEGER"
	case BOOLEAN:
		return "BOOLEAN"
	case BLOB:
		return "BYTEA"
	case VARCHAR:
		// assigns an arbitrary size if
		// none is provided.
		size := f.Size
		if size == 0 {
			size = 512
		}
		return fmt.Sprintf("VARCHAR(%d)", size)
	default:
		return
	}
}

func (d *posgres) Token(v int) (_ string) {
	switch v {
	case AUTO_INCREMENT:
		// postgres does not support the
		// auto-increment keyword.
		return
	case PRIMARY_KEY:
		return "PRIMARY KEY"
	default:
		return
	}
}

func (d *posgres) Param(i int) string {
	return fmt.Sprintf("$%d", i+1)
}


================================================
FILE: schema/dialect_sqlite.go
================================================
package schema

type sqlite struct {
	base
}

func newSqlite() Dialect {
	d := &sqlite{}
	d.base.Dialect = d
	return d
}


================================================
FILE: schema/helper.go
================================================
package schema

import (
	"strings"

	"github.com/acsellers/inflections"
	"github.com/drone/sqlgen/parse"
)

func Load(tree *parse.Node) *Table {
	table := new(Table)

	// local map of indexes, used for quick
	// lookups and de-duping.
	indexs := map[string]*Index{}

	// pluralizes the table name and then
	// formats in snake case.
	table.Name = inflections.Underscore(tree.Type)
	table.Name = inflections.Pluralize(table.Name)

	// each edge node in the tree is a column
	// in the table. Convert each edge node to
	// a Field structure.
	for _, node := range tree.Edges() {

		field := new(Field)

		// Lookup the SQL column type
		// TODO: move this to a function
		t, ok := parse.Types[node.Type]
		if ok {
			tt, ok := types[t]
			if !ok {
				tt = BLOB
			}
			field.Type = tt
		} else {
			field.Type = BLOB
		}

		// substitute tag variables
		if node.Tags != nil {

			if node.Tags.Skip {
				continue
			}

			// default ID and int64 to primary key
			// with auto-increment
			if node.Name == "ID" && node.Kind == parse.Int64 {
				node.Tags.Primary = true
				node.Tags.Auto = true
			}

			field.Auto = node.Tags.Auto
			field.Primary = node.Tags.Primary
			field.Size = node.Tags.Size

			if node.Tags.Primary {
				table.Primary = append(table.Primary, field)
			}

			if node.Tags.Index != "" {
				index, ok := indexs[node.Tags.Index]
				if !ok {
					index = new(Index)
					index.Name = node.Tags.Index
					indexs[index.Name] = index
					table.Index = append(table.Index, index)
				}
				index.Fields = append(index.Fields, field)
			}

			if node.Tags.Unique != "" {
				index, ok := indexs[node.Tags.Index]
				if !ok {
					index = new(Index)
					index.Name = node.Tags.Unique
					index.Unique = true
					indexs[index.Name] = index
					table.Index = append(table.Index, index)
				}
				index.Fields = append(index.Fields, field)
			}

			if node.Tags.Type != "" {
				t, ok := sqlTypes[node.Tags.Type]
				if ok {
					field.Type = t
				}
			}
		}

		// get the full path name
		path := node.Path()
		var parts []string
		for _, part := range path {
			if part.Tags != nil && part.Tags.Name != "" {
				parts = append(parts, part.Tags.Name)
				continue
			}

			parts = append(parts, part.Name)
		}
		field.Name = strings.Join(parts, "_")
		field.Name = inflections.Underscore(field.Name)

		table.Fields = append(table.Fields, field)
	}

	return table
}

// convert Go types to SQL types.
var types = map[uint8]int{
	parse.Bool:       BOOLEAN,
	parse.Int:        INTEGER,
	parse.Int8:       INTEGER,
	parse.Int16:      INTEGER,
	parse.Int32:      INTEGER,
	parse.Int64:      INTEGER,
	parse.Uint:       INTEGER,
	parse.Uint8:      INTEGER,
	parse.Uint16:     INTEGER,
	parse.Uint32:     INTEGER,
	parse.Uint64:     INTEGER,
	parse.Float32:    INTEGER,
	parse.Float64:    INTEGER,
	parse.Complex64:  INTEGER,
	parse.Complex128: INTEGER,
	parse.Interface:  BLOB,
	parse.Bytes:      BLOB,
	parse.String:     VARCHAR,
	parse.Map:        BLOB,
	parse.Slice:      BLOB,
}

var sqlTypes = map[string]int{
	"text":     VARCHAR,
	"varchar":  VARCHAR,
	"varchar2": VARCHAR,
	"number":   INTEGER,
	"integer":  INTEGER,
	"int":      INTEGER,
	"blob":     BLOB,
	"bytea":    BLOB,
}


================================================
FILE: schema/schema.go
================================================
package schema

// List of basic types
const (
	INTEGER int = iota
	VARCHAR
	BOOLEAN
	REAL
	BLOB
)

// List of vendor-specific keywords
const (
	AUTO_INCREMENT = iota
	PRIMARY_KEY
)

type Table struct {
	Name string

	Fields  []*Field
	Index   []*Index
	Primary []*Field
}

type Field struct {
	Name    string
	Type    int
	Primary bool
	Auto    bool
	Size    int
}

type Index struct {
	Name   string
	Unique bool

	Fields []*Field
}


================================================
FILE: tmpl.go
================================================
package main

// template to create a constant variable.
var sConst = `
const %s = %s
`

// template to wrap a string in multi-line quotes.
var sQuote = "`\n%s\n`"

// template to declare the package name.
var sPackage = `
package %s

// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.
`

// template to delcare the package imports.
var sImport = `
import (
	%s
)
`

// function template to scan a single row.
const sScanRow = `
func Scan%s(row *sql.Row) (*%s, error) {
	%s

	err := row.Scan(
		%s
	)
	if err != nil {
		return nil, err
	}

	v := &%s{}
	%s

	return v, nil
}
`

// function template to scan multiple rows.
const sScanRows = `
func Scan%s(rows *sql.Rows) ([]*%s, error) {
	var err error
	var vv []*%s

	%s
	for rows.Next() {
		err = rows.Scan(
			%s
		)
		if err != nil {
			return vv, err
		}

		v := &%s{}
		%s
		vv = append(vv, v)
	}
	return vv, rows.Err()
}
`

const sSliceRow = `
func Slice%s(v *%s) []interface{} {
	%s
	%s

	return []interface{}{
		%s
	}
}
`

const sSelectRow = `
func Select%s(db *sql.DB, query string, args ...interface{}) (*%s, error) {
	row := db.QueryRow(query, args...)
	return Scan%s(row)
}
`

// function template to select multiple rows.
const sSelectRows = `
func Select%s(db *sql.DB, query string, args ...interface{}) ([]*%s, error) {
	rows, err := db.Query(query, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	return Scan%s(rows)
}
`

// function template to insert a single row.
const sInsert = `
func Insert%s(db *sql.DB, query string, v *%s) error {

	res, err := db.Exec(query, Slice%s(v)[1:]...)
	if err != nil {
		return err
	}

	v.ID, err = res.LastInsertId()
	return err
}
`

// function template to update a single row.
const sUpdate = `
func Update%s(db *sql.DB, query string, v *%s) error {

	args := Slice%s(v)[1:]
	args = append(args, v.ID)
	_, err := db.Exec(query, args...)
	return err 
}
`


================================================
FILE: util.go
================================================
package main
Download .txt
gitextract_ds8o5qgz/

├── .gitignore
├── LICENSE
├── README.md
├── bench/
│   ├── type.go
│   ├── type_sql.go
│   └── type_test.go
├── demo/
│   ├── hook.go
│   ├── hook_sql.go
│   ├── issue.go
│   ├── issue_sql.go
│   ├── user.go
│   └── user_sql.go
├── fmt.go
├── gen.go
├── gen_funcs.go
├── gen_schema.go
├── parse/
│   ├── const.go
│   ├── node.go
│   ├── parse.go
│   ├── tag.go
│   └── tag_test.go
├── schema/
│   ├── base.go
│   ├── dialect.go
│   ├── dialect_mysql.go
│   ├── dialect_postgres.go
│   ├── dialect_sqlite.go
│   ├── helper.go
│   └── schema.go
├── tmpl.go
└── util.go
Download .txt
SYMBOL INDEX (189 symbols across 26 files)

FILE: bench/type.go
  type User (line 5) | type User struct

FILE: bench/type_sql.go
  function ScanUser (line 9) | func ScanUser(row *sql.Row) (*User, error) {
  function ScanUsers (line 43) | func ScanUsers(rows *sql.Rows) ([]*User, error) {
  function SliceUser (line 83) | func SliceUser(v *User) []interface{} {
  function SelectUser (line 111) | func SelectUser(db *sql.DB, query string, args ...interface{}) (*User, e...
  function SelectUsers (line 116) | func SelectUsers(db *sql.DB, query string, args ...interface{}) ([]*User...
  function InsertUser (line 125) | func InsertUser(db *sql.DB, query string, v *User) error {
  function UpdateUser (line 136) | func UpdateUser(db *sql.DB, query string, v *User) error {
  constant CreateUserStmt (line 144) | CreateUserStmt = `
  constant InsertUserStmt (line 156) | InsertUserStmt = `
  constant SelectUserStmt (line 167) | SelectUserStmt = `
  constant SelectUserRangeStmt (line 179) | SelectUserRangeStmt = `
  constant SelectUserCountStmt (line 192) | SelectUserCountStmt = `
  constant SelectUserPkeyStmt (line 197) | SelectUserPkeyStmt = `
  constant UpdateUserPkeyStmt (line 210) | UpdateUserPkeyStmt = `
  constant DeleteUserPkeyStmt (line 222) | DeleteUserPkeyStmt = `
  constant CreateUserNameStmt (line 227) | CreateUserNameStmt = `
  constant SelectUserNameStmt (line 231) | SelectUserNameStmt = `
  constant UpdateUserNameStmt (line 244) | UpdateUserNameStmt = `
  constant DeleteUserNameStmt (line 256) | DeleteUserNameStmt = `
  constant CreateUserEmailStmt (line 261) | CreateUserEmailStmt = `
  constant SelectUserEmailStmt (line 265) | SelectUserEmailStmt = `
  constant UpdateUserEmailStmt (line 278) | UpdateUserEmailStmt = `
  constant DeleteUserEmailStmt (line 290) | DeleteUserEmailStmt = `

FILE: bench/type_test.go
  function init (line 17) | func init() {
  function BenchmarkMeddlerRow (line 52) | func BenchmarkMeddlerRow(b *testing.B) {
  function BenchmarkMeddlerRows (line 66) | func BenchmarkMeddlerRows(b *testing.B) {
  function BenchmarkSqlxRow (line 79) | func BenchmarkSqlxRow(b *testing.B) {
  function BenchmarkSqlxRows (line 93) | func BenchmarkSqlxRows(b *testing.B) {
  function BenchmarkSqlgenRow (line 106) | func BenchmarkSqlgenRow(b *testing.B) {
  function BenchmarkSqlgenRows (line 119) | func BenchmarkSqlgenRows(b *testing.B) {

FILE: demo/hook.go
  type Hook (line 5) | type Hook struct
  type Commit (line 16) | type Commit struct
  type Author (line 24) | type Author struct

FILE: demo/hook_sql.go
  function ScanHook (line 9) | func ScanHook(row *sql.Row) (*Hook, error) {
  function ScanHooks (line 73) | func ScanHooks(rows *sql.Rows) ([]*Hook, error) {
  function SliceHook (line 143) | func SliceHook(v *Hook) []interface{} {
  function SelectHook (line 206) | func SelectHook(db *sql.DB, query string, args ...interface{}) (*Hook, e...
  function SelectHooks (line 211) | func SelectHooks(db *sql.DB, query string, args ...interface{}) ([]*Hook...
  function InsertHook (line 220) | func InsertHook(db *sql.DB, query string, v *Hook) error {
  function UpdateHook (line 231) | func UpdateHook(db *sql.DB, query string, v *Hook) error {
  constant CreateHookStmt (line 239) | CreateHookStmt = `
  constant InsertHookStmt (line 260) | InsertHookStmt = `
  constant SelectHookStmt (line 280) | SelectHookStmt = `
  constant SelectHookRangeStmt (line 301) | SelectHookRangeStmt = `
  constant SelectHookCountStmt (line 323) | SelectHookCountStmt = `
  constant SelectHookPkeyStmt (line 328) | SelectHookPkeyStmt = `
  constant UpdateHookPkeyStmt (line 350) | UpdateHookPkeyStmt = `
  constant DeleteHookPkeyStmt (line 371) | DeleteHookPkeyStmt = `

FILE: demo/issue.go
  type Issue (line 5) | type Issue struct

FILE: demo/issue_sql.go
  function ScanIssue (line 10) | func ScanIssue(row *sql.Row) (*Issue, error) {
  function ScanIssues (line 44) | func ScanIssues(rows *sql.Rows) ([]*Issue, error) {
  function SliceIssue (line 84) | func SliceIssue(v *Issue) []interface{} {
  function SelectIssue (line 112) | func SelectIssue(db *sql.DB, query string, args ...interface{}) (*Issue,...
  function SelectIssues (line 117) | func SelectIssues(db *sql.DB, query string, args ...interface{}) ([]*Iss...
  function InsertIssue (line 126) | func InsertIssue(db *sql.DB, query string, v *Issue) error {
  function UpdateIssue (line 137) | func UpdateIssue(db *sql.DB, query string, v *Issue) error {
  constant CreateIssueStmt (line 145) | CreateIssueStmt = `
  constant InsertIssueStmt (line 157) | InsertIssueStmt = `
  constant SelectIssueStmt (line 168) | SelectIssueStmt = `
  constant SelectIssueRangeStmt (line 180) | SelectIssueRangeStmt = `
  constant SelectIssueCountStmt (line 193) | SelectIssueCountStmt = `
  constant SelectIssuePkeyStmt (line 198) | SelectIssuePkeyStmt = `
  constant UpdateIssuePkeyStmt (line 211) | UpdateIssuePkeyStmt = `
  constant DeleteIssuePkeyStmt (line 223) | DeleteIssuePkeyStmt = `

FILE: demo/user.go
  type User (line 5) | type User struct

FILE: demo/user_sql.go
  function ScanUser (line 9) | func ScanUser(row *sql.Row) (*User, error) {
  function ScanUsers (line 49) | func ScanUsers(rows *sql.Rows) ([]*User, error) {
  function SliceUser (line 95) | func SliceUser(v *User) []interface{} {
  function SelectUser (line 129) | func SelectUser(db *sql.DB, query string, args ...interface{}) (*User, e...
  function SelectUsers (line 134) | func SelectUsers(db *sql.DB, query string, args ...interface{}) ([]*User...
  function InsertUser (line 143) | func InsertUser(db *sql.DB, query string, v *User) error {
  function UpdateUser (line 154) | func UpdateUser(db *sql.DB, query string, v *User) error {
  constant CreateUserStmt (line 162) | CreateUserStmt = `
  constant InsertUserStmt (line 176) | InsertUserStmt = `
  constant SelectUserStmt (line 189) | SelectUserStmt = `
  constant SelectUserRangeStmt (line 203) | SelectUserRangeStmt = `
  constant SelectUserCountStmt (line 218) | SelectUserCountStmt = `
  constant SelectUserPkeyStmt (line 223) | SelectUserPkeyStmt = `
  constant UpdateUserPkeyStmt (line 238) | UpdateUserPkeyStmt = `
  constant DeleteUserPkeyStmt (line 252) | DeleteUserPkeyStmt = `
  constant CreateUserLoginStmt (line 257) | CreateUserLoginStmt = `
  constant SelectUserLoginStmt (line 261) | SelectUserLoginStmt = `
  constant UpdateUserLoginStmt (line 276) | UpdateUserLoginStmt = `
  constant DeleteUserLoginStmt (line 290) | DeleteUserLoginStmt = `
  constant CreateUserEmailStmt (line 295) | CreateUserEmailStmt = `
  constant SelectUserEmailStmt (line 299) | SelectUserEmailStmt = `
  constant UpdateUserEmailStmt (line 314) | UpdateUserEmailStmt = `
  constant DeleteUserEmailStmt (line 328) | DeleteUserEmailStmt = `

FILE: fmt.go
  function format (line 11) | func format(in io.Reader) (io.Reader, error) {

FILE: gen.go
  function main (line 25) | func main() {

FILE: gen_funcs.go
  function writeImports (line 13) | func writeImports(w io.Writer, tree *parse.Node, pkgs ...string) {
  function writeSliceFunc (line 51) | func writeSliceFunc(w io.Writer, tree *parse.Node) {
  function writeRowFunc (line 125) | func writeRowFunc(w io.Writer, tree *parse.Node) {
  function writeRowsFunc (line 178) | func writeRowsFunc(w io.Writer, tree *parse.Node) {
  function writeSelectRow (line 231) | func writeSelectRow(w io.Writer, tree *parse.Node) {
  function writeSelectRows (line 235) | func writeSelectRows(w io.Writer, tree *parse.Node) {
  function writeInsertFunc (line 240) | func writeInsertFunc(w io.Writer, tree *parse.Node) {
  function writeUpdateFunc (line 246) | func writeUpdateFunc(w io.Writer, tree *parse.Node) {
  function join (line 252) | func join(nodes []*parse.Node, sep string) string {

FILE: gen_schema.go
  function writeSchema (line 14) | func writeSchema(w io.Writer, d schema.Dialect, t *schema.Table) {
  function writePackage (line 99) | func writePackage(w io.Writer, name string) {
  function writeConst (line 105) | func writeConst(w io.Writer, body string, label ...string) {

FILE: parse/const.go
  constant Invalid (line 4) | Invalid = iota
  constant Bool (line 5) | Bool
  constant Int (line 6) | Int
  constant Int8 (line 7) | Int8
  constant Int16 (line 8) | Int16
  constant Int32 (line 9) | Int32
  constant Int64 (line 10) | Int64
  constant Uint (line 11) | Uint
  constant Uint8 (line 12) | Uint8
  constant Uint16 (line 13) | Uint16
  constant Uint32 (line 14) | Uint32
  constant Uint64 (line 15) | Uint64
  constant Float32 (line 16) | Float32
  constant Float64 (line 17) | Float64
  constant Complex64 (line 18) | Complex64
  constant Complex128 (line 19) | Complex128
  constant Interface (line 20) | Interface
  constant Bytes (line 21) | Bytes
  constant Map (line 22) | Map
  constant Ptr (line 23) | Ptr
  constant String (line 24) | String
  constant Slice (line 25) | Slice
  constant Struct (line 26) | Struct

FILE: parse/node.go
  type Node (line 3) | type Node struct
    method append (line 14) | func (n *Node) append(node *Node) {
    method Walk (line 21) | func (n *Node) Walk(fn func(*Node)) {
    method WalkRev (line 31) | func (n *Node) WalkRev(fn func(*Node)) {
    method Edges (line 40) | func (n *Node) Edges() []*Node {
    method Path (line 52) | func (n *Node) Path() []*Node {

FILE: parse/parse.go
  function Parse (line 16) | func Parse(path, name string) (*Node, error) {
  function buildNodes (line 48) | func buildNodes(parent *Node, spec *ast.TypeSpec) error {
  function buildNode (line 64) | func buildNode(parent *Node, expr ast.Expr, name, tag string) error {

FILE: parse/tag.go
  constant EncodeGzip (line 12) | EncodeGzip = "gzip"
  constant EncodeJson (line 13) | EncodeJson = "json"
  type Tag (line 18) | type Tag struct
  function parseTag (line 32) | func parseTag(raw string) (*Tag, error) {

FILE: parse/tag_test.go
  function TestParseTag (line 42) | func TestParseTag(t *testing.T) {

FILE: schema/base.go
  type base (line 11) | type base struct
    method Table (line 16) | func (b *base) Table(t *Table) string {
    method Index (line 36) | func (b *base) Index(table *Table, index *Index) string {
    method Insert (line 44) | func (b *base) Insert(t *Table) string {
    method Update (line 60) | func (b *base) Update(t *Table, fields []*Field) string {
    method Delete (line 64) | func (b *base) Delete(t *Table, fields []*Field) string {
    method Select (line 68) | func (b *base) Select(t *Table, fields []*Field) string {
    method SelectRange (line 72) | func (b *base) SelectRange(t *Table, fields []*Field) string {
    method SelectCount (line 76) | func (b *base) SelectCount(t *Table, fields []*Field) string {
    method Param (line 82) | func (b *base) Param(i int) string {
    method Column (line 90) | func (b *base) Column(f *Field) string {
    method Token (line 106) | func (b *base) Token(v int) (_ string) {
    method columns (line 121) | func (b *base) columns(fields []*Field, inline, assign, def bool) stri...
    method columnw (line 128) | func (b *base) columnw(w io.Writer, fields []*Field, inline, assign, d...
    method clause (line 169) | func (b *base) clause(fields []*Field, pos int) string {

FILE: schema/dialect.go
  constant SQLITE (line 4) | SQLITE int = iota
  constant POSTGRES (line 5) | POSTGRES
  constant MYSQL (line 6) | MYSQL
  type Dialect (line 15) | type Dialect interface
  function New (line 29) | func New(dialect int) Dialect {

FILE: schema/dialect_mysql.go
  type mysql (line 7) | type mysql struct
    method Column (line 17) | func (d *mysql) Column(f *Field) (_ string) {
    method Token (line 38) | func (d *mysql) Token(v int) (_ string) {
  function newMysql (line 11) | func newMysql() Dialect {

FILE: schema/dialect_postgres.go
  type posgres (line 7) | type posgres struct
    method Column (line 17) | func (d *posgres) Column(f *Field) (_ string) {
    method Token (line 44) | func (d *posgres) Token(v int) (_ string) {
    method Param (line 57) | func (d *posgres) Param(i int) string {
  function newPosgres (line 11) | func newPosgres() Dialect {

FILE: schema/dialect_sqlite.go
  type sqlite (line 3) | type sqlite struct
  function newSqlite (line 7) | func newSqlite() Dialect {

FILE: schema/helper.go
  function Load (line 10) | func Load(tree *parse.Node) *Table {

FILE: schema/schema.go
  constant INTEGER (line 5) | INTEGER int = iota
  constant VARCHAR (line 6) | VARCHAR
  constant BOOLEAN (line 7) | BOOLEAN
  constant REAL (line 8) | REAL
  constant BLOB (line 9) | BLOB
  constant AUTO_INCREMENT (line 14) | AUTO_INCREMENT = iota
  constant PRIMARY_KEY (line 15) | PRIMARY_KEY
  type Table (line 18) | type Table struct
  type Field (line 26) | type Field struct
  type Index (line 34) | type Index struct

FILE: tmpl.go
  constant sScanRow (line 26) | sScanRow = `
  constant sScanRows (line 45) | sScanRows = `
  constant sSliceRow (line 67) | sSliceRow = `
  constant sSelectRow (line 78) | sSelectRow = `
  constant sSelectRows (line 86) | sSelectRows = `
  constant sInsert (line 98) | sInsert = `
  constant sUpdate (line 112) | sUpdate = `
Condensed preview — 30 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (68K chars).
[
  {
    "path": ".gitignore",
    "chars": 28,
    "preview": "sqlgen\n*.sqlite\n*.txt\n_docs\n"
  },
  {
    "path": "LICENSE",
    "chars": 1292,
    "preview": "Copyright (c) 2015, drone.io\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmo"
  },
  {
    "path": "README.md",
    "chars": 6038,
    "preview": "**sqlgen** generates SQL statements and database helper functions from your Go structs. It can be used in place of a sim"
  },
  {
    "path": "bench/type.go",
    "chars": 714,
    "preview": "package bench\n\n//go:generate ../sqlgen -file type.go -type User -pkg bench -o type_sql.go\n\ntype User struct {\n\tID      i"
  },
  {
    "path": "bench/type_sql.go",
    "chars": 4079,
    "preview": "package bench\n\n// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.\n\nimport (\n\t\"database/sql\"\n)\n\nfunc ScanUser(row *sql.Row) "
  },
  {
    "path": "bench/type_test.go",
    "chars": 2170,
    "preview": "package bench\n\nimport (\n\t\"database/sql\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/Pallinder/go-randomdata\"\n\t\"github.com/jmoiron/s"
  },
  {
    "path": "demo/hook.go",
    "chars": 505,
    "preview": "package demo\n\n//go:generate ../sqlgen -file hook.go -type Hook -pkg demo -o hook_sql.go -db mysql\n\ntype Hook struct {\n\tI"
  },
  {
    "path": "demo/hook_sql.go",
    "chars": 6531,
    "preview": "package demo\n\n// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.\n\nimport (\n\t\"database/sql\"\n)\n\nfunc ScanHook(row *sql.Row) ("
  },
  {
    "path": "demo/issue.go",
    "chars": 410,
    "preview": "package demo\n\n//go:generate ../sqlgen -file issue.go -type Issue -pkg demo -o issue_sql.go -db postgres\n\ntype Issue stru"
  },
  {
    "path": "demo/issue_sql.go",
    "chars": 3292,
    "preview": "package demo\n\n// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.\n\nimport (\n\t\"database/sql\"\n\t\"encoding/json\"\n)\n\nfunc ScanIss"
  },
  {
    "path": "demo/user.go",
    "chars": 433,
    "preview": "package demo\n\n//go:generate ../sqlgen -file user.go -type User -pkg demo -o user_sql.go\n\ntype User struct {\n\tID     int6"
  },
  {
    "path": "demo/user_sql.go",
    "chars": 4551,
    "preview": "package demo\n\n// THIS FILE WAS AUTO-GENERATED. DO NOT MODIFY.\n\nimport (\n\t\"database/sql\"\n)\n\nfunc ScanUser(row *sql.Row) ("
  },
  {
    "path": "fmt.go",
    "chars": 313,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n)\n\n// format formats a template using gofmt.\nfunc format(in io.Re"
  },
  {
    "path": "gen.go",
    "chars": 2103,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/drone/sqlgen/parse\"\n\t\"github.com/drone/sqlgen/s"
  },
  {
    "path": "gen_funcs.go",
    "chars": 5605,
    "preview": "package main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/acsellers/inflections\"\n\t\"github.com/drone/sqlgen/p"
  },
  {
    "path": "gen_schema.go",
    "chars": 2297,
    "preview": "package main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\n\t\"bitbucket.org/pkg/inflect\"\n\t\"github.com/drone/sqlgen/schema\"\n)\n\n// wri"
  },
  {
    "path": "parse/const.go",
    "chars": 671,
    "preview": "package parse\n\nconst (\n\tInvalid = iota\n\tBool\n\tInt\n\tInt8\n\tInt16\n\tInt32\n\tInt64\n\tUint\n\tUint8\n\tUint16\n\tUint32\n\tUint64\n\tFloat"
  },
  {
    "path": "parse/node.go",
    "chars": 1214,
    "preview": "package parse\n\ntype Node struct {\n\tPkg  string // source code package.\n\tName string // source code name.\n\tKind uint8  //"
  },
  {
    "path": "parse/parse.go",
    "chars": 2923,
    "preview": "package parse\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"go/ast\"\n\t\"go/parser\"\n\t\"go/token\"\n)\n\nvar (\n\tErrTypeNotFound = errors.New(\"Cann"
  },
  {
    "path": "parse/tag.go",
    "chars": 1143,
    "preview": "package parse\n\nimport (\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"gopkg.in/yaml.v2\"\n)\n\nconst (\n\tEncodeGzip = \"gzip\"\n\tEncodeJson = "
  },
  {
    "path": "parse/tag_test.go",
    "chars": 827,
    "preview": "package parse\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nvar tagTests = []struct {\n\traw string\n\ttag *Tag\n}{\n\t{\n\t\t`sql:\"-\"`,\n\t\t&T"
  },
  {
    "path": "schema/base.go",
    "chars": 4614,
    "preview": "package schema\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"text/tabwriter\"\n)\n\ntype base struct {\n\tDialect Dialect\n}\n\n//"
  },
  {
    "path": "schema/dialect.go",
    "chars": 657,
    "preview": "package schema\n\nconst (\n\tSQLITE int = iota\n\tPOSTGRES\n\tMYSQL\n)\n\nvar Dialects = map[string]int{\n\t\"sqlite\":   SQLITE,\n\t\"pos"
  },
  {
    "path": "schema/dialect_mysql.go",
    "chars": 662,
    "preview": "package schema\n\nimport (\n\t\"fmt\"\n)\n\ntype mysql struct {\n\tbase\n}\n\nfunc newMysql() Dialect {\n\td := &mysql{}\n\td.base.Dialect"
  },
  {
    "path": "schema/dialect_postgres.go",
    "chars": 893,
    "preview": "package schema\n\nimport (\n\t\"fmt\"\n)\n\ntype posgres struct {\n\tbase\n}\n\nfunc newPosgres() Dialect {\n\td := &posgres{}\n\td.base.D"
  },
  {
    "path": "schema/dialect_sqlite.go",
    "chars": 121,
    "preview": "package schema\n\ntype sqlite struct {\n\tbase\n}\n\nfunc newSqlite() Dialect {\n\td := &sqlite{}\n\td.base.Dialect = d\n\treturn d\n}"
  },
  {
    "path": "schema/helper.go",
    "chars": 3206,
    "preview": "package schema\n\nimport (\n\t\"strings\"\n\n\t\"github.com/acsellers/inflections\"\n\t\"github.com/drone/sqlgen/parse\"\n)\n\nfunc Load(t"
  },
  {
    "path": "schema/schema.go",
    "chars": 435,
    "preview": "package schema\n\n// List of basic types\nconst (\n\tINTEGER int = iota\n\tVARCHAR\n\tBOOLEAN\n\tREAL\n\tBLOB\n)\n\n// List of vendor-sp"
  },
  {
    "path": "tmpl.go",
    "chars": 1875,
    "preview": "package main\n\n// template to create a constant variable.\nvar sConst = `\nconst %s = %s\n`\n\n// template to wrap a string in"
  },
  {
    "path": "util.go",
    "chars": 13,
    "preview": "package main\n"
  }
]

About this extraction

This page contains the full source code of the drone/sqlgen GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 30 files (58.2 KB), approximately 18.3k tokens, and a symbol index with 189 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!