diff --git a/Makefile b/Makefile index b86fd5c..7678f67 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,11 @@ -build/gojson: format test - mkdir -p build - go build -o build/gojson ./gojson +build: format test + go build -o build/gojson cmd/gojson/gojson.go test: go test -v format: - gofmt -w -e -s -l *.go **/*.go + gofmt -w -e -s -l *.go **.go clean: rm -rf build diff --git a/README.md b/README.md index 08b111f..aca8274 100644 --- a/README.md +++ b/README.md @@ -100,23 +100,15 @@ type Repository struct { } ``` -CLI Installation +Installation ---------------- ```sh -$ go get github.com/ChimeraCoder/gojson/gojson +$ go get github.com/ChimeraCoder/gojson/... ``` Assuming `$GOPATH/bin` is in your `PATH`, you can now invoke `gojson` directly. - -API Installation ----------------- - -```sh -$ go get github.com/ChimeraCoder/gojson -``` - Development ----------- @@ -126,24 +118,36 @@ $ cd gojson $ go test ``` -**Building CLI** +**Building** ``` -$ go build -o _build/gojson ./gojson +$ make build ``` -**Installing CLI** +**Formatting** ``` -$ go install ./gojson +$ make format ``` -**Formatting** +**Testing** ``` -$ gofmt -w -e -s -l . +$ make test ``` + +Documentation +----------- +Use godoc to see further documentation + +``` +godoc -http=:6060 +``` + +open browser to http://localhost:6060/pkg/github.com/ChimeraCoder/gojson/ + + Related Work ------------ diff --git a/cmd/gojson/gojson.go b/cmd/gojson/gojson.go new file mode 100644 index 0000000..ab0b563 --- /dev/null +++ b/cmd/gojson/gojson.go @@ -0,0 +1,64 @@ +package main + +import ( + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + + "github.com/ChimeraCoder/gojson" +) + +var ( + name = flag.String("name", "Foo", "the name of the struct") + pkg = flag.String("pkg", "main", "the name of the package for the generated code") + inputName = flag.String("input", "", "the name of the input file containing JSON (if input not provided via STDIN)") + outputName = flag.String("o", "", "the name of the file to write the output to (outputs to STDOUT by default)") +) + +func main() { + flag.Parse() + + if isInteractive() && *inputName == "" { + flag.Usage() + fmt.Fprintln(os.Stderr, "Expects input on stdin") + os.Exit(1) + } + + var input io.Reader + input = os.Stdin + if *inputName != "" { + f, err := os.Open(*inputName) + if err != nil { + log.Fatalf("reading input file: %s", err) + } + defer f.Close() + input = f + } + + if output, err := gojson.Generate(input, *name, *pkg); err != nil { + fmt.Fprintln(os.Stderr, "error parsing", err) + os.Exit(1) + } else { + if *outputName != "" { + err := ioutil.WriteFile(*outputName, output, 0644) + if err != nil { + log.Fatalf("writing output: %s", err) + } + } else { + fmt.Print(string(output)) + } + } + +} + +// Return true if os.Stdin appears to be interactive +func isInteractive() bool { + fileInfo, err := os.Stdin.Stat() + if err != nil { + return false + } + return fileInfo.Mode()&(os.ModeCharDevice|os.ModeCharDevice) != 0 +} diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..db72de9 --- /dev/null +++ b/doc.go @@ -0,0 +1,115 @@ +/* +GoJson generates go struct defintions from JSON documents + +It reads from stdin and prints to stdout. + +Usage: + gojson [flags] + +The flags are: + -name + the name of the struct + -pkg + the name of the package for the generated code + -inputName + the name of the input file containing JSON (if input not provided via STDIN) + -outputName + the name of the file to write the output to (outputs to STDOUT by default) + +Examples + +Convert resulting json of an endpoint, +giving the resulting struct the name "Repository" + + curl -s https://api.github.com/repos/chimeracoder/gojson | gojson -name=Repository + +Output: + + type Repository struct { + ArchiveURL string `json:"archive_url"` + AssigneesURL string `json:"assignees_url"` + BlobsURL string `json:"blobs_url"` + BranchesURL string `json:"branches_url"` + CloneURL string `json:"clone_url"` + CollaboratorsURL string `json:"collaborators_url"` + CommentsURL string `json:"comments_url"` + CommitsURL string `json:"commits_url"` + CompareURL string `json:"compare_url"` + ContentsURL string `json:"contents_url"` + ContributorsURL string `json:"contributors_url"` + CreatedAt string `json:"created_at"` + DefaultBranch string `json:"default_branch"` + Description string `json:"description"` + DownloadsURL string `json:"downloads_url"` + EventsURL string `json:"events_url"` + Fork bool `json:"fork"` + Forks float64 `json:"forks"` + ForksCount float64 `json:"forks_count"` + ForksURL string `json:"forks_url"` + FullName string `json:"full_name"` + GitCommitsURL string `json:"git_commits_url"` + GitRefsURL string `json:"git_refs_url"` + GitTagsURL string `json:"git_tags_url"` + GitURL string `json:"git_url"` + HasDownloads bool `json:"has_downloads"` + HasIssues bool `json:"has_issues"` + HasWiki bool `json:"has_wiki"` + Homepage interface{} `json:"homepage"` + HooksURL string `json:"hooks_url"` + HtmlURL string `json:"html_url"` + ID float64 `json:"id"` + IssueCommentURL string `json:"issue_comment_url"` + IssueEventsURL string `json:"issue_events_url"` + IssuesURL string `json:"issues_url"` + KeysURL string `json:"keys_url"` + LabelsURL string `json:"labels_url"` + Language string `json:"language"` + LanguagesURL string `json:"languages_url"` + MasterBranch string `json:"master_branch"` + MergesURL string `json:"merges_url"` + MilestonesURL string `json:"milestones_url"` + MirrorURL interface{} `json:"mirror_url"` + Name string `json:"name"` + NetworkCount float64 `json:"network_count"` + NotificationsURL string `json:"notifications_url"` + OpenIssues float64 `json:"open_issues"` + OpenIssuesCount float64 `json:"open_issues_count"` + Owner struct { + AvatarURL string `json:"avatar_url"` + EventsURL string `json:"events_url"` + FollowersURL string `json:"followers_url"` + FollowingURL string `json:"following_url"` + GistsURL string `json:"gists_url"` + GravatarID string `json:"gravatar_id"` + HtmlURL string `json:"html_url"` + ID float64 `json:"id"` + Login string `json:"login"` + OrganizationsURL string `json:"organizations_url"` + ReceivedEventsURL string `json:"received_events_url"` + ReposURL string `json:"repos_url"` + SiteAdmin bool `json:"site_admin"` + StarredURL string `json:"starred_url"` + SubscriptionsURL string `json:"subscriptions_url"` + Type string `json:"type"` + URL string `json:"url"` + } `json:"owner"` + Private bool `json:"private"` + PullsURL string `json:"pulls_url"` + PushedAt string `json:"pushed_at"` + Size float64 `json:"size"` + SshURL string `json:"ssh_url"` + StargazersURL string `json:"stargazers_url"` + StatusesURL string `json:"statuses_url"` + SubscribersURL string `json:"subscribers_url"` + SubscriptionURL string `json:"subscription_url"` + SvnURL string `json:"svn_url"` + TagsURL string `json:"tags_url"` + TeamsURL string `json:"teams_url"` + TreesURL string `json:"trees_url"` + UpdatedAt string `json:"updated_at"` + URL string `json:"url"` + Watchers float64 `json:"watchers"` + WatchersCount float64 `json:"watchers_count"` + } +*/ +package gojson diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..4dd109f --- /dev/null +++ b/example_test.go @@ -0,0 +1,25 @@ +package gojson_test + +import ( + "fmt" + "strings" + + "github.com/ChimeraCoder/gojson" +) + +func ExampleGenerate() { + structName := "test" + pkgName := "main" + input := strings.NewReader(`{"sample":"json"}`) + goStruct, err := gojson.Generate(input, structName, pkgName) + if err != nil { + fmt.Printf("Error generating json: %s", err.Error()) + } + fmt.Printf("%s", goStruct) + // Output: + // package main + // + // type test struct { + // Sample string `json:"sample"` + // } +} diff --git a/gojson/gojson.go b/gojson/gojson.go deleted file mode 100644 index 04ba71c..0000000 --- a/gojson/gojson.go +++ /dev/null @@ -1,106 +0,0 @@ -// gojson generates go struct defintions from JSON documents -// -// Reads from stdin and prints to stdout -// -// Example: -// curl -s https://api.github.com/repos/chimeracoder/gojson | gojson -name=Repository -// -// Output: -// package main -// -// type User struct { -// AvatarURL string `json:"avatar_url"` -// Bio interface{} `json:"bio"` -// Blog string `json:"blog"` -// Company string `json:"company"` -// CreatedAt string `json:"created_at"` -// Email string `json:"email"` -// EventsURL string `json:"events_url"` -// Followers float64 `json:"followers"` -// FollowersURL string `json:"followers_url"` -// Following float64 `json:"following"` -// FollowingURL string `json:"following_url"` -// GistsURL string `json:"gists_url"` -// GravatarID string `json:"gravatar_id"` -// Hireable bool `json:"hireable"` -// HtmlURL string `json:"html_url"` -// ID float64 `json:"id"` -// Location string `json:"location"` -// Login string `json:"login"` -// Name string `json:"name"` -// OrganizationsURL string `json:"organizations_url"` -// PublicGists float64 `json:"public_gists"` -// PublicRepos float64 `json:"public_repos"` -// ReceivedEventsURL string `json:"received_events_url"` -// ReposURL string `json:"repos_url"` -// StarredURL string `json:"starred_url"` -// SubscriptionsURL string `json:"subscriptions_url"` -// Type string `json:"type"` -// UpdatedAt string `json:"updated_at"` -// URL string `json:"url"` -// } - -package main - -import ( - "flag" - "fmt" - "io" - "io/ioutil" - "log" - "os" - - . "github.com/ChimeraCoder/gojson" -) - -var ( - name = flag.String("name", "Foo", "the name of the struct") - pkg = flag.String("pkg", "main", "the name of the package for the generated code") - inputName = flag.String("input", "", "the name of the input file containing JSON (if input not provided via STDIN)") - outputName = flag.String("o", "", "the name of the file to write the output to (outputs to STDOUT by default)") -) - -func main() { - flag.Parse() - - if isInteractive() && *inputName == "" { - flag.Usage() - fmt.Fprintln(os.Stderr, "Expects input on stdin") - os.Exit(1) - } - - var input io.Reader - input = os.Stdin - if *inputName != "" { - f, err := os.Open(*inputName) - if err != nil { - log.Fatalf("reading input file: %s", err) - } - defer f.Close() - input = f - } - - if output, err := Generate(input, *name, *pkg); err != nil { - fmt.Fprintln(os.Stderr, "error parsing", err) - os.Exit(1) - } else { - if *outputName != "" { - err := ioutil.WriteFile(*outputName, output, 0644) - if err != nil { - log.Fatalf("writing output: %s", err) - } - } else { - fmt.Print(string(output)) - } - } - -} - -// Return true if os.Stdin appears to be interactive -func isInteractive() bool { - fileInfo, err := os.Stdin.Stat() - if err != nil { - return false - } - return fileInfo.Mode()&(os.ModeCharDevice|os.ModeCharDevice) != 0 -} diff --git a/json-to-array_test.go b/json-to-array_test.go index 39328ff..5707861 100644 --- a/json-to-array_test.go +++ b/json-to-array_test.go @@ -1,4 +1,4 @@ -package json2struct +package gojson import ( "os" @@ -7,7 +7,7 @@ import ( // Test example document func TestExampleArray(t *testing.T) { - i, err := os.Open("example_array.json") + i, err := os.Open("testdata/array.input") if err != nil { t.Error("error opening example.json", err) } diff --git a/json-to-struct.go b/json-to-struct.go index 125bfd5..6f44f64 100644 --- a/json-to-struct.go +++ b/json-to-struct.go @@ -1,100 +1,4 @@ -// gojson generates go struct defintions from JSON documents -// -// Reads from stdin and prints to stdout -// -// Example: -// curl -s https://api.github.com/repos/chimeracoder/gojson | gojson -name=Repository -// -// Output: -// package main -// -// type Repository struct { -// ArchiveURL string `json:"archive_url"` -// AssigneesURL string `json:"assignees_url"` -// BlobsURL string `json:"blobs_url"` -// BranchesURL string `json:"branches_url"` -// CloneURL string `json:"clone_url"` -// CollaboratorsURL string `json:"collaborators_url"` -// CommentsURL string `json:"comments_url"` -// CommitsURL string `json:"commits_url"` -// CompareURL string `json:"compare_url"` -// ContentsURL string `json:"contents_url"` -// ContributorsURL string `json:"contributors_url"` -// CreatedAt string `json:"created_at"` -// DefaultBranch string `json:"default_branch"` -// Description string `json:"description"` -// DownloadsURL string `json:"downloads_url"` -// EventsURL string `json:"events_url"` -// Fork bool `json:"fork"` -// Forks float64 `json:"forks"` -// ForksCount float64 `json:"forks_count"` -// ForksURL string `json:"forks_url"` -// FullName string `json:"full_name"` -// GitCommitsURL string `json:"git_commits_url"` -// GitRefsURL string `json:"git_refs_url"` -// GitTagsURL string `json:"git_tags_url"` -// GitURL string `json:"git_url"` -// HasDownloads bool `json:"has_downloads"` -// HasIssues bool `json:"has_issues"` -// HasWiki bool `json:"has_wiki"` -// Homepage interface{} `json:"homepage"` -// HooksURL string `json:"hooks_url"` -// HtmlURL string `json:"html_url"` -// ID float64 `json:"id"` -// IssueCommentURL string `json:"issue_comment_url"` -// IssueEventsURL string `json:"issue_events_url"` -// IssuesURL string `json:"issues_url"` -// KeysURL string `json:"keys_url"` -// LabelsURL string `json:"labels_url"` -// Language string `json:"language"` -// LanguagesURL string `json:"languages_url"` -// MasterBranch string `json:"master_branch"` -// MergesURL string `json:"merges_url"` -// MilestonesURL string `json:"milestones_url"` -// MirrorURL interface{} `json:"mirror_url"` -// Name string `json:"name"` -// NetworkCount float64 `json:"network_count"` -// NotificationsURL string `json:"notifications_url"` -// OpenIssues float64 `json:"open_issues"` -// OpenIssuesCount float64 `json:"open_issues_count"` -// Owner struct { -// AvatarURL string `json:"avatar_url"` -// EventsURL string `json:"events_url"` -// FollowersURL string `json:"followers_url"` -// FollowingURL string `json:"following_url"` -// GistsURL string `json:"gists_url"` -// GravatarID string `json:"gravatar_id"` -// HtmlURL string `json:"html_url"` -// ID float64 `json:"id"` -// Login string `json:"login"` -// OrganizationsURL string `json:"organizations_url"` -// ReceivedEventsURL string `json:"received_events_url"` -// ReposURL string `json:"repos_url"` -// SiteAdmin bool `json:"site_admin"` -// StarredURL string `json:"starred_url"` -// SubscriptionsURL string `json:"subscriptions_url"` -// Type string `json:"type"` -// URL string `json:"url"` -// } ` json:"owner"` -// Private bool `json:"private"` -// PullsURL string `json:"pulls_url"` -// PushedAt string `json:"pushed_at"` -// Size float64 `json:"size"` -// SshURL string `json:"ssh_url"` -// StargazersURL string `json:"stargazers_url"` -// StatusesURL string `json:"statuses_url"` -// SubscribersURL string `json:"subscribers_url"` -// SubscriptionURL string `json:"subscription_url"` -// SvnURL string `json:"svn_url"` -// TagsURL string `json:"tags_url"` -// TeamsURL string `json:"teams_url"` -// TreesURL string `json:"trees_url"` -// UpdatedAt string `json:"updated_at"` -// URL string `json:"url"` -// Watchers float64 `json:"watchers"` -// WatchersCount float64 `json:"watchers_count"` -// } -package json2struct +package gojson import ( "encoding/json" diff --git a/json-to-struct_test.go b/json-to-struct_test.go index 0b50ac5..0ccb81e 100644 --- a/json-to-struct_test.go +++ b/json-to-struct_test.go @@ -1,6 +1,7 @@ -package json2struct +package gojson import ( + "bytes" "io/ioutil" "os" "strings" @@ -42,12 +43,12 @@ func TestInvalidFieldChars(t *testing.T) { // Test example document func TestExample(t *testing.T) { - i, err := os.Open("example.json") + i, err := os.Open("testdata/struct.input") if err != nil { t.Error("error opening example.json", err) } - expected, err := ioutil.ReadFile("expected_output_test.go") + expected, err := ioutil.ReadFile("testdata/struct.golden") if err != nil { t.Error("error reading expected_output_test.go", err) } @@ -56,9 +57,8 @@ func TestExample(t *testing.T) { if err != nil { t.Error(err) } - sactual, sexpected := string(actual), string(expected) - if sactual != sexpected { - t.Errorf("'%s' (expected) != '%s' (actual)", sexpected, sactual) + if !bytes.Equal(actual, expected) { + t.Errorf("'%s' (expected) != '%s' (actual)", expected, actual) } } diff --git a/expected_output_array_test.go b/testdata/array.golden similarity index 97% rename from expected_output_array_test.go rename to testdata/array.golden index 0e5cf43..e53a4cb 100644 --- a/expected_output_array_test.go +++ b/testdata/array.golden @@ -1,6 +1,6 @@ package json2struct -// This is what we would like example_array.json +// This is what we would like array.input // to generate, though for the time being we'll have to settle // for []interface{} // https://github.com/ChimeraCoder/gojson/issues/17 diff --git a/example_array.json b/testdata/array.input similarity index 100% rename from example_array.json rename to testdata/array.input diff --git a/expected_output_test.go b/testdata/struct.golden similarity index 100% rename from expected_output_test.go rename to testdata/struct.golden diff --git a/example.json b/testdata/struct.input similarity index 100% rename from example.json rename to testdata/struct.input