diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c9ea7d..01a5263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## [1.1.1] - 2017-08-16 +### Fixed +- fix comments bugs + +### Changed +- refactoring + + ## [1.1.0] - 2017-08-15 ### Added - add description comments @@ -152,7 +160,8 @@ - pre-release -[1.0.2]: https://github.com/aaharu/schemarshal/compare/1.0.2...1.1.0 +[1.1.1]: https://github.com/aaharu/schemarshal/compare/1.1.0...1.1.1 +[1.1.0]: https://github.com/aaharu/schemarshal/compare/1.0.2...1.1.0 [1.0.2]: https://github.com/aaharu/schemarshal/compare/1.0.1...1.0.2 [1.0.1]: https://github.com/aaharu/schemarshal/compare/1.0.0...1.0.1 [1.0.0]: https://github.com/aaharu/schemarshal/compare/0.9.0...1.0.0 diff --git a/README.md b/README.md index 2bb374a..3de2a9f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ curl -s "https://raw.githubusercontent.com/aaharu/schemarshal/master/test_data/d ## [Examples](examples.md) -- [example a.json](examples.md#a.json) +- [example a.json](examples.md#ajson) - [example qiita schema](examples.md#qiita-v2-schema) ## Dependencies diff --git a/codegen/generator.go b/codegen/generator.go index c8b7b78..8cc73a1 100644 --- a/codegen/generator.go +++ b/codegen/generator.go @@ -20,27 +20,32 @@ import ( ) // Generator of Go source code from JSON Schema -type Generator struct { +type Generator interface { + ReadSchema(input io.Reader, name string) error + Generate() ([]byte, error) +} + +type generator struct { name string // package nage command string - imports ImportSpec + imports importSpec decls []*typeSpec - enumList EnumSpec + enumList enumSpec } // NewGenerator create Generator struct -func NewGenerator(packageName string, command string) *Generator { - return &Generator{ +func NewGenerator(packageName string, command string) Generator { + return &generator{ name: packageName, command: command, - imports: ImportSpec{}, + imports: importSpec{}, decls: []*typeSpec{}, - enumList: EnumSpec{}, + enumList: enumSpec{}, } } // ReadSchema : read and parse JSON Schema -func (g *Generator) ReadSchema(input io.Reader, name string) error { +func (g *generator) ReadSchema(input io.Reader, name string) error { js, err := schema.Read(input) if err != nil { return err @@ -58,8 +63,8 @@ func (g *Generator) ReadSchema(input io.Reader, name string) error { } // parse returns JSON Schema type -func (g *Generator) parse(js *schema.Schema, fieldName string) (JSONType, error) { - jt := JSONType{} +func (g *generator) parse(js *schema.Schema, fieldName string) (jsonType, error) { + jt := jsonType{} if inPrimitiveTypes(schema.IntegerType, js.Type) || inPrimitiveTypes(schema.BooleanType, js.Type) || inPrimitiveTypes(schema.NumberType, js.Type) { @@ -147,7 +152,7 @@ func (g *Generator) parse(js *schema.Schema, fieldName string) (JSONType, error) if propType.format == formatObject { objectTypeName := fieldName + utils.UpperCamelCase(key) + "Object" g.addType(objectTypeName, &propType) - copyType := &JSONType{ + copyType := jsonType{ format: propType.format, nullable: propType.nullable, fields: propType.fields, @@ -159,7 +164,7 @@ func (g *Generator) parse(js *schema.Schema, fieldName string) (JSONType, error) name: utils.UpperCamelCase(key), description: propSchema.Description, jsontype: copyType, - jsontag: &jsonTag{ + jsontag: jsonTag{ name: key, omitEmpty: !js.IsPropRequired(key), }, @@ -168,8 +173,8 @@ func (g *Generator) parse(js *schema.Schema, fieldName string) (JSONType, error) jt.addField(&field{ name: utils.UpperCamelCase(key), description: propSchema.Description, - jsontype: &propType, - jsontag: &jsonTag{ + jsontype: propType, + jsontag: jsonTag{ name: key, omitEmpty: !js.IsPropRequired(key), }, @@ -188,7 +193,7 @@ func (g *Generator) parse(js *schema.Schema, fieldName string) (JSONType, error) } // addType add a type statement -func (g *Generator) addType(name string, jsonType *JSONType) { +func (g *generator) addType(name string, jsonType *jsonType) { g.decls = append(g.decls, &typeSpec{ name: name, jsontype: jsonType, @@ -196,9 +201,8 @@ func (g *Generator) addType(name string, jsonType *JSONType) { } // Generate gofmt-ed Go source code -func (g *Generator) Generate() ([]byte, error) { +func (g *generator) Generate() ([]byte, error) { var buf bytes.Buffer - buf.WriteString(fmt.Sprintf("// Code generated by %s `%s`\n", version.String(), g.command)) buf.WriteString("// DO NOT RECOMMEND EDITING THIS FILE.\n\n") buf.WriteString(fmt.Sprintf("package %s\n\n", g.name)) @@ -225,7 +229,7 @@ func (g *Generator) Generate() ([]byte, error) { if g.decls != nil { for i := range g.decls { if g.decls[i].jsontype.description != "" { - buf.WriteString(fmt.Sprintf("// %s : %s\n", g.decls[i].name, g.decls[i].jsontype.description)) + buf.WriteString(fmt.Sprintf("// %s : %s\n", g.decls[i].name, utils.CleanDescription(g.decls[i].jsontype.description))) } buf.WriteString("type " + g.decls[i].name + " ") g.decls[i].jsontype.nullable = false @@ -234,106 +238,109 @@ func (g *Generator) Generate() ([]byte, error) { } } - if g.enumList != nil && len(g.enumList) > 0 { - buf.WriteString("\n") - // sort map - var keys []string - for k := range g.enumList { - keys = append(keys, k) - } - sort.Strings(keys) - for _, typeName := range keys { - enum := g.enumList[typeName] - buf.WriteString("type " + typeName + " int\n") - buf.WriteString("const (\n") - for i := range enum { - buf.WriteString(utils.UpperCamelCase(fmt.Sprintf("%s %v", typeName, enum[i]))) - if i == 0 { - buf.WriteString(" " + typeName + " = iota\n") - } else { - buf.WriteString("\n") - } - } - buf.WriteString(")\n\n") - - var enumMapName = "_" + strings.ToLower(typeName[:1]) - if len(typeName) > 1 { - enumMapName += typeName[1:] - } - buf.WriteString("var " + enumMapName + " = map[" + typeName + "]interface{}{\n") - for i := range enum { - buf.WriteString(utils.UpperCamelCase(fmt.Sprintf("%s %v", typeName, enum[i])) + ": ") - switch v := enum[i].(type) { - case string: - buf.WriteString(strconv.Quote(v)) - default: - buf.WriteString(fmt.Sprintf("%v", v)) - } - buf.WriteString(",\n") - } - buf.WriteString("}\n\n") - - buf.WriteString("func (enum " + typeName + ") MarshalJSON() ([]byte, error) {\n") - buf.WriteString("switch v:= " + enumMapName + "[enum].(type) {\n") - buf.WriteString("case string:\n") - buf.WriteString("return []byte(strconv.Quote(v)), nil\n") - buf.WriteString("default:\n") - buf.WriteString("return []byte(fmt.Sprintf(\"%v\", v)), nil\n") - buf.WriteString("}\n") - buf.WriteString("}\n\n") - - buf.WriteString("func (enum *" + typeName + ") UnmarshalJSON(data []byte) error {\n") - buf.WriteString("for i, v := range " + enumMapName + " {\n") - buf.WriteString("switch vv := v.(type) {\n") - buf.WriteString("case string:\n") - buf.WriteString("if strconv.Quote(vv) == string(data) {\n") - buf.WriteString("*enum = " + typeName + "(i)\n") - buf.WriteString("return nil\n") - buf.WriteString("}\n") - buf.WriteString("default:\n") - buf.WriteString("if fmt.Sprintf(\"%v\", v) == string(data) {\n") - buf.WriteString("*enum = " + typeName + "(i)\n") - buf.WriteString("return nil\n") - buf.WriteString("}\n") - buf.WriteString("}\n") - buf.WriteString("}\n") - buf.WriteString("return fmt.Errorf(\"Error: miss-matched " + typeName + " (%s)\", data)\n") - buf.WriteString("}\n\n") - - buf.WriteString("func (enum " + typeName + ") String() string {\n") - buf.WriteString("switch v:= " + enumMapName + "[enum].(type) {\n") - buf.WriteString("case string:\n") - buf.WriteString("return v\n") - buf.WriteString("default:\n") - buf.WriteString("return fmt.Sprintf(\"%v\", v)\n") - buf.WriteString("}\n") - buf.WriteString("}\n\n") - - buf.WriteString("func To" + typeName + "(val interface{}) *" + typeName + " {\n") - buf.WriteString("for i, v := range " + enumMapName + " {\n") - buf.WriteString("if val == v {") - buf.WriteString("return &i") - buf.WriteString("}\n") - buf.WriteString("}\n") - buf.WriteString("return nil") - buf.WriteString("}\n\n") - } - } + buf.Write(g.enumList.generate()) //return buf.Bytes(), nil return format.Source(buf.Bytes()) } -// ImportSpec has `import` information -type ImportSpec map[string]string +// importSpec has `import` information +type importSpec map[string]string type typeSpec struct { name string // type name - jsontype *JSONType + jsontype *jsonType } -// EnumSpec has enum information -type EnumSpec map[string][]interface{} +// enumSpec has enum information +type enumSpec map[string][]interface{} + +func (e enumSpec) generate() []byte { + var buf bytes.Buffer + // sort map + var keys []string + for k := range e { + keys = append(keys, k) + } + sort.Strings(keys) + for _, typeName := range keys { + enum := e[typeName] + buf.WriteString("\ntype " + typeName + " int\n") + buf.WriteString("const (\n") + for i := range enum { + buf.WriteString(utils.UpperCamelCase(fmt.Sprintf("%s %v", typeName, enum[i]))) + if i == 0 { + buf.WriteString(" " + typeName + " = iota\n") + } else { + buf.WriteString("\n") + } + } + buf.WriteString(")\n\n") + + var enumMapName = "_" + strings.ToLower(typeName[:1]) + if len(typeName) > 1 { + enumMapName += typeName[1:] + } + buf.WriteString("var " + enumMapName + " = map[" + typeName + "]interface{}{\n") + for i := range enum { + buf.WriteString(utils.UpperCamelCase(fmt.Sprintf("%s %v", typeName, enum[i])) + ": ") + switch v := enum[i].(type) { + case string: + buf.WriteString(strconv.Quote(v)) + default: + buf.WriteString(fmt.Sprintf("%v", v)) + } + buf.WriteString(",\n") + } + buf.WriteString("}\n\n") + + buf.WriteString("func (enum " + typeName + ") MarshalJSON() ([]byte, error) {\n") + buf.WriteString("switch v:= " + enumMapName + "[enum].(type) {\n") + buf.WriteString("case string:\n") + buf.WriteString("return []byte(strconv.Quote(v)), nil\n") + buf.WriteString("default:\n") + buf.WriteString("return []byte(fmt.Sprintf(\"%v\", v)), nil\n") + buf.WriteString("}\n") + buf.WriteString("}\n\n") + + buf.WriteString("func (enum *" + typeName + ") UnmarshalJSON(data []byte) error {\n") + buf.WriteString("for i, v := range " + enumMapName + " {\n") + buf.WriteString("switch vv := v.(type) {\n") + buf.WriteString("case string:\n") + buf.WriteString("if strconv.Quote(vv) == string(data) {\n") + buf.WriteString("*enum = " + typeName + "(i)\n") + buf.WriteString("return nil\n") + buf.WriteString("}\n") + buf.WriteString("default:\n") + buf.WriteString("if fmt.Sprintf(\"%v\", v) == string(data) {\n") + buf.WriteString("*enum = " + typeName + "(i)\n") + buf.WriteString("return nil\n") + buf.WriteString("}\n") + buf.WriteString("}\n") + buf.WriteString("}\n") + buf.WriteString("return fmt.Errorf(\"Error: miss-matched " + typeName + " (%s)\", data)\n") + buf.WriteString("}\n\n") + + buf.WriteString("func (enum " + typeName + ") String() string {\n") + buf.WriteString("switch v:= " + enumMapName + "[enum].(type) {\n") + buf.WriteString("case string:\n") + buf.WriteString("return v\n") + buf.WriteString("default:\n") + buf.WriteString("return fmt.Sprintf(\"%v\", v)\n") + buf.WriteString("}\n") + buf.WriteString("}\n\n") + + buf.WriteString("func To" + typeName + "(val interface{}) *" + typeName + " {\n") + buf.WriteString("for i, v := range " + enumMapName + " {\n") + buf.WriteString("if val == v {") + buf.WriteString("return &i") + buf.WriteString("}\n") + buf.WriteString("}\n") + buf.WriteString("return nil") + buf.WriteString("}\n") + } + return buf.Bytes() +} type jsonFormat int @@ -347,22 +354,22 @@ const ( formatDatetime ) -// JSONType is type of json -type JSONType struct { +// jsonType is type of json +type jsonType struct { format jsonFormat nullable bool fields []*field // object has - itemType *JSONType // array has + itemType *jsonType // array has typeName string // object's array and object has enumType string // enum has description string // for comment } -func (t *JSONType) addField(f *field) { +func (t *jsonType) addField(f *field) { t.fields = append(t.fields, f) } -func (t *JSONType) generate() []byte { +func (t *jsonType) generate() []byte { var buf bytes.Buffer if t.nullable { buf.WriteString("*") @@ -379,7 +386,7 @@ func (t *JSONType) generate() []byte { buf.WriteString("struct {\n") for i := range t.fields { if t.fields[i].description != "" { - buf.WriteString(fmt.Sprintf("// %s : %s\n", t.fields[i].name, t.fields[i].description)) + buf.WriteString(fmt.Sprintf("// %s : %s\n", t.fields[i].name, utils.CleanDescription(t.fields[i].description))) } buf.WriteString(t.fields[i].name) buf.WriteString(" ") @@ -414,9 +421,9 @@ func (t *JSONType) generate() []byte { type field struct { name string - description string // comment - jsontype *JSONType // go type - jsontag *jsonTag // `json:""` + description string // comment + jsontype jsonType // go type + jsontag jsonTag // `json:""` } type jsonTag struct { diff --git a/glide.lock b/glide.lock index a15716c..1069853 100644 --- a/glide.lock +++ b/glide.lock @@ -1,14 +1,14 @@ hash: 5d3ef523a7bb34322bc57c775a712625a7096f295c05d182f1f7dcb04a417901 -updated: 2017-01-23T00:29:24.784436222+09:00 +updated: 2017-08-15T02:14:03.765066079+09:00 imports: - name: github.com/davecgh/go-spew - version: 346938d642f2ec3594ed81d874461961cd0faa76 + version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9 subpackages: - spew - name: github.com/lestrrat/go-jspointer version: f4881e611bdbe9fb413a7780721ef8400a1f2341 - name: github.com/lestrrat/go-jsref - version: e452c7b5801d1c6494c9e7e0cbc7498c0f88dfd1 + version: 50df7b2d07d799426a9ac43fa24bdb4785f72a54 subpackages: - provider - name: github.com/lestrrat/go-jsschema @@ -18,13 +18,14 @@ imports: - name: github.com/lestrrat/go-structinfo version: f74c056fe41f860aa6264478c664a6fff8a64298 - name: github.com/pkg/errors - version: 248dadf4e9068a0b3e79f02ed0a610d935de5302 + version: c605e284fe17294bda444b34710735b29d1a9d90 - name: golang.org/x/crypto - version: b8a2a83acfe6e6770b75de42d5ff4c67596675c0 + version: b176d7def5d71bdd214203491f89843ed217f420 subpackages: - ssh/terminal - name: golang.org/x/sys - version: d75a52659825e75fff6158388dddc6a5b04f9ba5 + version: e42485b6e20ae7d2304ec72e535b103ed350cc02 subpackages: - unix + - windows testImports: [] diff --git a/test_data/disk.json b/test_data/disk.json index 0151cc3..58488af 100644 --- a/test_data/disk.json +++ b/test_data/disk.json @@ -15,6 +15,9 @@ "type": "string", "enum": [ "x", "y" ] } + }, + "num": { + "type": "number" } }, "required": [ "type", "device" ], diff --git a/utils/converter.go b/utils/converter.go index 9357e36..2ca5fbb 100644 --- a/utils/converter.go +++ b/utils/converter.go @@ -30,6 +30,15 @@ func UpperCamelCase(str string) string { return result } +// CleanDescription remove \n, \r and \t +func CleanDescription(desc string) string { + desc = strings.TrimSpace(desc) + desc = strings.Replace(desc, "\n", " ", -1) + desc = strings.Replace(desc, "\r", " ", -1) + desc = strings.Replace(desc, "\t", " ", -1) + return desc +} + // FileName returns file-name without ext func FileName(file *os.File) string { name := path.Base(file.Name()) diff --git a/version/version.go b/version/version.go index ad8e826..e55dd96 100644 --- a/version/version.go +++ b/version/version.go @@ -7,7 +7,7 @@ package version import "fmt" // Version of schemarshal -const Version = "1.1.0" +const Version = "1.1.1" // String return ` ` func String() string { diff --git a/version/version_test.go b/version/version_test.go index f06d76e..bed5da1 100644 --- a/version/version_test.go +++ b/version/version_test.go @@ -8,7 +8,7 @@ import "testing" func TestString(t *testing.T) { actual := String() - expected := "schemarshal 1.1.0" + expected := "schemarshal 1.1.1" if actual != expected { t.Errorf("got %v\nwant %v", actual, expected) }