func main() { name := os.Args[1] buf, err := ioutil.ReadFile(name) if err != nil { panic(err) } var schema Schema err = yaml.Unmarshal(buf, &schema) if err != nil { panic(err) } fmt.Printf("properties=%v\n", schema.Properties) fmt.Printf("\n\n/////\n") tag := strings.Split(schema.Tag, "/") tname := strings.Split(tag[len(tag)-1], "-")[0] fmt.Printf("type %s struct {\n", snaker.SnakeToCamel(tname)) for k, prop := range schema.Properties { fmt.Printf("\t%s %v `yaml:\"%s\"` // %q\n", snaker.SnakeToCamel(k), prop.Type, k, strings.TrimSpace(prop.Description), ) //fmt.Printf("// %#v\n", prop) } fmt.Printf("}\n") }
func printer(cfg map[string]interface{}) (dst string) { for k, v := range cfg { switch v.(type) { case int64: dst += fmt.Sprintf(" %s int `toml:\"%s\"`\n", snaker.SnakeToCamel(k), k) case string: dst += fmt.Sprintf(" %s string `toml:\"%s\"`\n", snaker.SnakeToCamel(k), k) case bool: dst += fmt.Sprintf(" %s bool `toml:\"%s\"`\n", snaker.SnakeToCamel(k), k) case []map[string]interface{}: vv := v.([]map[string]interface{}) if len(vv) > 0 { dst += fmt.Sprintf(" %s []struct {\n", snaker.SnakeToCamel(k)) dst += printer(vv[0]) dst += fmt.Sprintf(" }\n") } case map[string]interface{}: dst += fmt.Sprintf(" %s struct {\n", snaker.SnakeToCamel(k)) dst += printer(v.(map[string]interface{})) dst += fmt.Sprintf(" }\n") default: panic(fmt.Sprintf("Unknown type: %T", v)) } } return }
// PgLoadTables loads the table definitions from the database, writing the // resulting templates to typeMap and returning the created templates.TableTemplates. func PgLoadTables(args *internal.ArgType, db *sql.DB, typeMap map[string]*bytes.Buffer) (map[string]*templates.TableTemplate, error) { var err error // load columns cols, err := models.ColumnsByRelkindSchema(db, "r", args.Schema) if err != nil { return nil, err } // process columns fieldMap := map[string]map[string]bool{} tableMap := map[string]*templates.TableTemplate{} for _, c := range cols { if _, ok := fieldMap[c.TableName]; !ok { fieldMap[c.TableName] = map[string]bool{} } // set col info c.Field = snaker.SnakeToCamel(c.ColumnName) c.Len, c.NilType, c.Type = PgParseType(args, c.DataType, c.IsNullable) // set value in table map if not present if _, ok := tableMap[c.TableName]; !ok { tableMap[c.TableName] = &templates.TableTemplate{ Type: inflector.Singularize(snaker.SnakeToCamel(c.TableName)), TableSchema: args.Schema, TableName: c.TableName, Fields: []*models.Column{}, } } // set primary key if c.IsPrimaryKey { tableMap[c.TableName].PrimaryKey = c.ColumnName tableMap[c.TableName].PrimaryKeyField = c.Field tableMap[c.TableName].PrimaryKeyType = c.Type } // append col to template fields if _, ok := fieldMap[c.TableName][c.ColumnName]; !ok { tableMap[c.TableName].Fields = append(tableMap[c.TableName].Fields, c) } fieldMap[c.TableName][c.ColumnName] = true } // generate table templates for _, t := range tableMap { buf := GetBuf(typeMap, strings.ToLower(t.Type)) err = templates.Tpls["postgres.model.go.tpl"].Execute(buf, t) if err != nil { return nil, err } } return tableMap, nil }
// PgLoadEnums reads the enums from the database, writing the values to the // typeMap and returning the created EnumTemplates. func PgLoadEnums(args *internal.ArgType, db *sql.DB, typeMap map[string]*bytes.Buffer) (map[string]*templates.EnumTemplate, error) { var err error // load enums enums, err := models.EnumsBySchema(db, args.Schema) if err != nil { return nil, err } // process enums enumMap := map[string]*templates.EnumTemplate{} for _, e := range enums { // calc go type and add to known types goType := inflector.Singularize(snaker.SnakeToCamel(e.EnumType)) templates.KnownTypeMap[goType] = true // calculate val, chopping off redundant type name if applicable val := snaker.SnakeToCamel(strings.ToLower(e.EnumValue)) if strings.HasSuffix(strings.ToLower(val), strings.ToLower(goType)) { v := val[:len(val)-len(goType)] if len(v) > 0 { val = v } } // copy values back into model e.Value = val e.Type = goType // set value in enum map if not present typ := strings.ToLower(goType) if _, ok := enumMap[typ]; !ok { enumMap[typ] = &templates.EnumTemplate{ Type: goType, EnumType: e.EnumType, Values: []*models.Enum{}, } } // append enum to template vals enumMap[typ].Values = append(enumMap[typ].Values, e) } // generate enum templates for _, e := range enumMap { buf := GetBuf(typeMap, strings.ToLower(e.Type)) err = templates.Tpls["postgres.enum.go.tpl"].Execute(buf, e) if err != nil { return nil, err } } return enumMap, nil }
// PgLoadProcs reads the procs from the database, writing the values to the // typeMap and returning the created ProcTemplates. func PgLoadProcs(args *internal.ArgType, db *sql.DB, typeMap map[string]*bytes.Buffer) (map[string]*templates.ProcTemplate, error) { var err error // load procs procs, err := models.ProcsBySchema(db, args.Schema) if err != nil { return nil, err } // process procs procMap := map[string]*templates.ProcTemplate{} for _, p := range procs { // fix the name if it starts with underscore name := p.ProcName if name[0:1] == "_" { name = name[1:] } // create template procTpl := templates.ProcTemplate{ Name: snaker.SnakeToCamel(name), TableSchema: args.Schema, ProcName: p.ProcName, ProcParameterTypes: p.ParameterTypes, ProcReturnType: p.ReturnType, Parameters: []*models.Column{}, } // parse return type into template _, procTpl.NilReturnType, procTpl.ReturnType = PgParseType(args, p.ReturnType, false) // split parameter types if len(p.ParameterTypes) > 0 { for i, paramType := range strings.Split(p.ParameterTypes, ", ") { // determine the go equivalent parameter types and add to parameters _, _, pt := PgParseType(args, paramType, false) procTpl.Parameters = append(procTpl.Parameters, &models.Column{ Field: "v" + strconv.Itoa(i), Type: pt, }) } } procMap[p.ProcName] = &procTpl } // generate proc templates for _, p := range procMap { buf := GetBuf(typeMap, strings.ToLower("sp_"+p.Name)) err = templates.Tpls["postgres.proc.go.tpl"].Execute(buf, p) if err != nil { return nil, err } } return procMap, nil }
// fixFieldName changes field names like // 'id' -> 'ID' func fixFieldName(field *gogo.FieldDescriptorProto) { if gogoproto.IsCustomName(field) { return // Skip if a custom name is specified. } if field.Options == nil { field.Options = new(gogo.FieldOptions) } name := field.GetName() name = snaker.SnakeToCamel(name) name = lintName(name) // Use gogoproto.customname proto.SetExtension(field.Options, gogoproto.E_Customname, &name) }
// SnakeToIdentifier wraps snaker.SnakeToCamel and adds logic specific for xo and go. func SnakeToIdentifier(s string) string { // lowercase s = strings.ToLower(s) // replace bad chars with _ s = replaceBadChars(s) // remove leading/trailing underscores s = strings.TrimLeft(s, "_") s = strings.TrimRight(s, "_") // fix 2 or more __ s = underscoreRE.ReplaceAllString(s, "_") return snaker.SnakeToCamel(s) }
func fillWithDetails(strukt interface{}, only []string, except []string) { elem := reflect.ValueOf(strukt).Elem() elemT := reflect.TypeOf(strukt).Elem() for i := 0; i < elem.NumField(); i++ { field := elem.Field(i) fieldt := elemT.Field(i) fakeType := fieldt.Tag.Get("fako") if fakeType != "" { fakeType = snaker.SnakeToCamel(fakeType) function := findFakeFunctionFor(fakeType) inOnly := len(only) == 0 || (len(only) > 0 && contains(only, fieldt.Name)) notInExcept := len(except) == 0 || (len(except) > 0 && !contains(except, fieldt.Name)) if field.CanSet() && fakeType != "" && inOnly && notInExcept { field.SetString(function()) } } } }
// PgParseQuery parses the query and generates a type for it. func PgParseQuery(args *internal.ArgType, db *sql.DB, typeMap map[string]*bytes.Buffer) error { var err error // parse supplied query queryStr, params := parseQuery(args, args.Query, "$%d") inspectStr, _ := parseQuery(args, args.Query, "NULL") // strip out if args.QueryStrip { queryStr = queryStripRE.ReplaceAllString(queryStr, "") } // split up query and inspect based on lines query := strings.Split(queryStr, "\n") inspect := strings.Split(inspectStr, "\n") // build query comments with stripped values // FIXME: this is off by one, because of golang template syntax limitations queryComments := make([]string, len(query)+1) if args.QueryStrip { for i, l := range inspect { pos := queryStripRE.FindStringIndex(l) if pos != nil { queryComments[i+1] = l[pos[0]:pos[1]] } else { queryComments[i+1] = "" } } } // trim whitespace if applicable if args.QueryTrim { for n, l := range query { query[n] = strings.TrimSpace(l) if n < len(query)-1 { query[n] = query[n] + " " } } for n, l := range inspect { inspect[n] = strings.TrimSpace(l) if n < len(inspect)-1 { inspect[n] = inspect[n] + " " } } } // create temporary view xoid xoid := "_xo_" + genRandomID() viewq := `CREATE TEMPORARY VIEW ` + xoid + ` AS (` + strings.Join(inspect, "\n") + `)` _, err = db.Exec(viewq) if err != nil { return err } // determine schema name temporary view was created on // sql query var nspq = `SELECT n.nspname ` + `FROM pg_class c ` + `JOIN pg_namespace n ON n.oid = c.relnamespace ` + `WHERE n.nspname LIKE 'pg_temp%' AND c.relname = $1` // run schema query var schema string err = db.QueryRow(nspq, xoid).Scan(&schema) if err != nil { return err } // load column information ("v" == view) cols, err := models.ColumnsByRelkindSchema(db, "v", schema) if err != nil { return err } // create template for query type typeTpl := &templates.TableTemplate{ Type: args.QueryType, TableSchema: args.Schema, Fields: []*models.Column{}, Comment: args.QueryTypeComment, } // process columns for _, c := range cols { c.Field = snaker.SnakeToCamel(c.ColumnName) c.Len, c.NilType, c.Type = PgParseType(args, c.DataType, false) typeTpl.Fields = append(typeTpl.Fields, c) } // generate query type template buf := GetBuf(typeMap, strings.ToLower(args.QueryType)) err = templates.Tpls["postgres.model.go.tpl"].Execute(buf, typeTpl) if err != nil { return err } // build func name funcName := args.QueryFunc if funcName == "" { // no func name specified, so generate based on type if args.QueryOnlyOne { funcName = args.QueryType } else { funcName = inflector.Pluralize(args.QueryType) } // affix any params if len(params) == 0 { funcName = "Get" + funcName } else { funcName = funcName + "By" for _, p := range params { funcName = funcName + strings.ToUpper(p[0][:1]) + p[0][1:] } } } // create func template funcTpl := &templates.FuncTemplate{ Name: funcName, Type: args.QueryType, Query: query, QueryComments: queryComments, Parameters: params, OnlyOne: args.QueryOnlyOne, Comment: args.QueryFuncComment, Table: typeTpl, } // generate template err = templates.Tpls["postgres.func.go.tpl"].Execute(buf, funcTpl) if err != nil { return err } return nil }
var ep spec.Endpoint if err := json.NewDecoder(f).Decode(&ep); err != nil { return err } return cliTemplate.Execute(os.Stdout, map[string]interface{}{ "spec": ep, }) } var funcs = template.FuncMap{ "methodName": snaker.SnakeToCamel, "optionsName": func(str string) string { return snaker.SnakeToCamel(str) + "Options" }, "flag": func(typ, name, setVar, varName string) string { switch typ { case "bool": return fmt.Sprintf(`%s.BoolVar(&%s, "%s", false, "")`, setVar, varName, name) case "string": return fmt.Sprintf(`%s.StringVar(&%s, "%s", "", "")`, setVar, varName, name) case "int": return fmt.Sprintf(`%s.IntVar(&%s, "%s", 0, "")`, setVar, varName, name) default: panic("Invalid type " + typ) } }, }
// PgParseType parse a postgres type into a Go type based on the column // definition. func PgParseType(args *internal.ArgType, dt string, nullable bool) (int, string, string) { precision := 0 nilType := "nil" asSlice := false // handle SETOF if strings.HasPrefix(dt, "SETOF ") { _, _, t := PgParseType(args, dt[len("SETOF "):], false) return 0, "nil", "[]" + t } // determine if it's a slice if strings.HasSuffix(dt, "[]") { dt = dt[:len(dt)-2] asSlice = true } // extract length if loc := pgLenRE.FindStringIndex(dt); len(loc) != 0 { precision, _ = strconv.Atoi(dt[loc[0]+1 : loc[1]-1]) dt = dt[:loc[0]] } var typ string switch dt { case "boolean": nilType = "false" typ = "bool" if nullable { nilType = "sql.NullBool{}" typ = "sql.NullBool" } case "character", "character varying", "text": nilType = `""` typ = "string" if nullable { nilType = "sql.NullString{}" typ = "sql.NullString" } case "smallint": nilType = "0" typ = "int16" if nullable { nilType = "sql.NullInt64{}" typ = "sql.NullInt64" } case "integer": nilType = "0" typ = args.Int32Type if nullable { nilType = "sql.NullInt64{}" typ = "sql.NullInt64" } case "bigint": nilType = "0" typ = "int64" if nullable { nilType = "sql.NullInt64{}" typ = "sql.NullInt64" } case "smallserial": nilType = "0" typ = "uint16" if nullable { nilType = "sql.NullInt64{}" typ = "sql.NullInt64" } case "serial": nilType = "0" typ = args.Uint32Type if nullable { nilType = "sql.NullInt64{}" typ = "sql.NullInt64" } case "bigserial": nilType = "0" typ = "uint64" if nullable { nilType = "sql.NullInt64{}" typ = "sql.NullInt64" } case "real": nilType = "0.0" typ = "float32" if nullable { nilType = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "double precision": nilType = "0.0" typ = "float64" if nullable { nilType = "sql.NullFloat64{}" typ = "sql.NullFloat64" } case "bytea": asSlice = true typ = "byte" case "timestamp with time zone": typ = "*time.Time" if nullable { nilType = "pq.NullTime{}" typ = "pq.NullTime" } case "time with time zone", "time without time zone", "timestamp without time zone": nilType = "0" typ = "int64" if nullable { nilType = "sql.NullInt64{}" typ = "sql.NullInt64" } case "interval": typ = "*time.Duration" case `"char"`, "bit": // FIXME: this needs to actually be tested ... // i think this should be 'rune' but I don't think database/sql // supports 'rune' as a type? // // this is mainly here because postgres's pg_catalog.* meta tables have // this as a type. //typ = "rune" nilType = `uint8(0)` typ = "uint8" case `"any"`, "bit varying": asSlice = true typ = "byte" default: if strings.HasPrefix(dt, args.Schema+".") { // in the same schema, so chop off typ = snaker.SnakeToCamel(dt[len(args.Schema)+1:]) nilType = typ + "(0)" } else { typ = snaker.SnakeToCamel(dt) nilType = typ + "{}" } } // special case for []slice if typ == "string" && asSlice { return precision, "StringSlice{}", "StringSlice" } // correct type if slice if asSlice { typ = "[]" + typ nilType = "nil" } return precision, nilType, typ }
// PgLoadIdx loads indexes from the database. func PgLoadIdx(args *internal.ArgType, db *sql.DB, typeMap map[string]*bytes.Buffer, tableMap map[string]*templates.TableTemplate) (map[string]*templates.IdxTemplate, error) { var err error // load idx's idxMap := map[string]*templates.IdxTemplate{} for _, t := range tableMap { // find relevant columns fields := []*models.Column{} for _, f := range t.Fields { if f.IsIndex && !f.IsForeignKey { if _, ok := idxMap[f.IndexName]; !ok { i := &templates.IdxTemplate{ Type: t.Type, Name: snaker.SnakeToCamel(f.IndexName), TableSchema: t.TableSchema, TableName: f.TableName, IndexName: f.IndexName, IsUnique: f.IsUnique, Fields: fields, Table: t, } // non unique lookup if !f.IsUnique { idxName := i.IndexName // chop off tablename_ if strings.HasPrefix(idxName, f.TableName+"_") { idxName = idxName[len(f.TableName)+1:] } // chop off _idx or _index switch { case strings.HasSuffix(idxName, "_idx"): idxName = idxName[:len(idxName)-len("_idx")] case strings.HasSuffix(idxName, "_index"): idxName = idxName[:len(idxName)-len("_index")] } i.Name = snaker.SnakeToCamel(idxName) i.Plural = inflector.Pluralize(t.Type) } idxMap[f.IndexName] = i } idxMap[f.IndexName].Fields = append(idxMap[f.IndexName].Fields, f) } } } // idx keys idxKeys := []string{} for k := range idxMap { idxKeys = append(idxKeys, k) } sort.Strings(idxKeys) // generate templates for _, k := range idxKeys { buf := GetBuf(typeMap, strings.ToLower(idxMap[k].Type)) err = templates.Tpls["postgres.idx.go.tpl"].Execute(buf, idxMap[k]) if err != nil { return nil, err } } return idxMap, nil }
return err } return apiTemplate.Execute(os.Stdout, map[string]interface{}{ "spec": ep, }) } func ucfirst(str string) string { f, i := utf8.DecodeRuneInString(str) return string(unicode.ToUpper(f)) + str[i:] } var funcs = template.FuncMap{ "responseName": func(str string) string { return snaker.SnakeToCamel(str) + "Response" }, "optionsName": func(str string) string { return snaker.SnakeToCamel(str) + "Options" }, "methodName": snaker.SnakeToCamel, "rsName": func(sp, rs string) string { return snaker.SnakeToCamel(sp) + ucfirst(rs) }, "encodeFunc": func(str string) string { return "encode" + ucfirst(str) }, "fieldName": func(str string) string { return ucfirst(snaker.SnakeToCamel(str)) }, "fieldNameFromRS": func(str string) string {
func run() int { var ( args = flag.Args() g genex.Generator ) // Print the header and package clause. g.Printf("// Code generated by gen-config.\n") // Run generate for each type. filename := []string{} config := map[string]interface{}{} for _, fp := range genex.MustParseGlobs(args[:]) { if strings.HasSuffix(fp, ".tml") { if _, err := toml.DecodeFile(fp, &config); err == nil { g.Printf("// %s\n", fp) filename = append(filename, fp) } } } g.Printf("// DO NOT EDIT\n") g.Printf("\npackage %s\n", *pkg) // Generate two source files. params := map[string]interface{}{} params["ConfigDir"] = *confDir params["TypeBody"] = printer(config) params["Filename"] = func() (ret []string) { for _, v := range filename { ret = append(ret, strings.Replace(v, *confDir+"/", "", -1)) } return }() params["StructName"] = func(cfg map[string]interface{}) (ret []map[string]string) { for k, _ := range cfg { elm := map[string]string{} elm["Struct"] = snaker.SnakeToCamel(k) elm["Tag"] = fmt.Sprintf("`toml:\"%s\"`", k) ret = append(ret, elm) } return }(config) // Format the output. t := template.Must(template.New("").Parse(text)) var buf bytes.Buffer t.Execute(&buf, params) g.Printf("%s", buf.String()) src := g.Format() // Write to file. outputName := *output if outputName == "" { baseName := OUTPUT outputName = filepath.Join(*srcDir, strings.ToLower(baseName)) } err := ioutil.WriteFile(outputName, src, 0644) if err != nil { log.Fatalf("writing output: %s", err) } return 0 }
// Register allows user to add his own data generators for special cases // that we could not cover with the generators that fako includes by default. func Register(identifier string, generator func() string) { fakeType := snaker.SnakeToCamel(identifier) customGenerators[fakeType] = generator }
func ColumnToField(s string) string { return snaker.SnakeToCamel(s) }
func PropertyToField(s string) string { mediary := snaker.CamelToSnake(s) return snaker.SnakeToCamel(mediary) }