From 40ecdac242d6100e4bebec0a23b80ae814f3dccd Mon Sep 17 00:00:00 2001 From: Thomas Pelletier Date: Tue, 30 May 2017 18:33:25 -0700 Subject: [PATCH] Clean up documentation (#168) Fixes #135 --- README.md | 86 ++++++++++++++++++++++---------------------- cmd/tomljson/main.go | 5 +++ cmd/tomll/main.go | 5 +++ doc.go | 77 +++++---------------------------------- doc_test.go | 28 +++++++++++++-- marshal.go | 36 +++++++++---------- marshal_test.go | 19 ++++++++++ toml.go | 3 +- 8 files changed, 124 insertions(+), 135 deletions(-) diff --git a/README.md b/README.md index 8279102..22da41a 100644 --- a/README.md +++ b/README.md @@ -17,64 +17,62 @@ Go-toml provides the following features for using data parsed from TOML document * Load TOML documents from files and string data * Easily navigate TOML structure using Tree +* Mashaling and unmarshaling to and from data structures * Line & column position data for all parsed elements * [Query support similar to JSON-Path](query/) * Syntax errors contain line and column numbers -Go-toml is designed to help cover use-cases not covered by reflection-based TOML parsing: - -* Semantic evaluation of parsed TOML -* Informing a user of mistakes in the source document, after it has been parsed -* Programatic handling of default values on a case-by-case basis -* Using a TOML document as a flexible data-store - ## Import - import "github.com/pelletier/go-toml" - -## Usage - -### Example - -Say you have a TOML file that looks like this: - -```toml -[postgres] -user = "pelletier" -password = "mypassword" +```go +import "github.com/pelletier/go-toml" ``` -Read the username and password like this: +## Usage example + +Read a TOML document: ```go -import ( - "fmt" - "github.com/pelletier/go-toml" -) +config, _ := toml.LoadString(` +[postgres] +user = "pelletier" +password = "mypassword"`) +// retrieve data directly +user := config.Get("postgres.user").(string) -config, err := toml.LoadFile("config.toml") -if err != nil { - fmt.Println("Error ", err.Error()) -} else { - // retrieve data directly - user := config.Get("postgres.user").(string) - password := config.Get("postgres.password").(string) +// or using an intermediate object +postgresConfig := config.Get("postgres").(*toml.Tree) +password = postgresConfig.Get("password").(string) +``` - // or using an intermediate object - configTree := config.Get("postgres").(*toml.Tree) - user = configTree.Get("user").(string) - password = configTree.Get("password").(string) - fmt.Println("User is ", user, ". Password is ", password) +Or use Unmarshal: - // show where elements are in the file - fmt.Println("User position: %v", configTree.GetPosition("user")) - fmt.Println("Password position: %v", configTree.GetPosition("password")) +```go +type Postgres struct { + User string + Password string +} +type Config struct { + Postgres Postgres +} - // use a query to gather elements without walking the tree - results, _ := config.Query("$..[user,password]") - for ii, item := range results.Values() { - fmt.Println("Query result %d: %v", ii, item) - } +doc := []byte(` +[postgres] +user = "pelletier" +password = "mypassword"`) + +config := Config{} +Unmarshal(doc, &config) +fmt.Println("user=", config.Postgres.User) +``` + +Or use a query: + +```go +// use a query to gather elements without walking the tree +results, _ := config.Query("$..[user,password]") +for ii, item := range results.Values() { + fmt.Println("Query result %d: %v", ii, item) } ``` diff --git a/cmd/tomljson/main.go b/cmd/tomljson/main.go index 8bfe462..b2d6fc6 100644 --- a/cmd/tomljson/main.go +++ b/cmd/tomljson/main.go @@ -1,3 +1,8 @@ +// Tomljson reads TOML and converts to JSON. +// +// Usage: +// cat file.toml | tomljson > file.json +// tomljson file1.toml > file.json package main import ( diff --git a/cmd/tomll/main.go b/cmd/tomll/main.go index f185c56..36c7e37 100644 --- a/cmd/tomll/main.go +++ b/cmd/tomll/main.go @@ -1,3 +1,8 @@ +// Tomll is a linter for TOML +// +// Usage: +// cat file.toml | tomll > file_linted.toml +// tomll file1.toml file2.toml # lint the two files in place package main import ( diff --git a/doc.go b/doc.go index cfa5e4a..3c89619 100644 --- a/doc.go +++ b/doc.go @@ -1,79 +1,18 @@ -// Package toml is a TOML markup language parser. +// Package toml is a TOML parser and manipulation library. // // This version supports the specification as described in // https://github.com/toml-lang/toml/blob/master/versions/en/toml-v0.4.0.md // -// TOML Parsing +// Marshaling // -// TOML data may be parsed in two ways: by file, or by string. +// Go-toml can marshal and unmarshal TOML documents from and to data +// structures. // -// // load TOML data by filename -// tree, err := toml.LoadFile("filename.toml") +// TOML document as a tree // -// // load TOML data stored in a string -// tree, err := toml.Load(stringContainingTomlData) -// -// Either way, the result is a Tree object that can be used to navigate the -// structure and data within the original document. -// -// -// Getting data from the Tree -// -// After parsing TOML data with Load() or LoadFile(), use the Has() and Get() -// methods on the returned Tree, to find your way through the document data. -// -// if tree.Has("foo") { -// fmt.Println("foo is:", tree.Get("foo")) -// } -// -// Working with Paths -// -// Go-toml has support for basic dot-separated key paths on the Has(), Get(), Set() -// and GetDefault() methods. These are the same kind of key paths used within the -// TOML specification for struct tames. -// -// // looks for a key named 'baz', within struct 'bar', within struct 'foo' -// tree.Has("foo.bar.baz") -// -// // returns the key at this path, if it is there -// tree.Get("foo.bar.baz") -// -// TOML allows keys to contain '.', which can cause this syntax to be problematic -// for some documents. In such cases, use the GetPath(), HasPath(), and SetPath(), -// methods to explicitly define the path. This form is also faster, since -// it avoids having to parse the passed key for '.' delimiters. -// -// // looks for a key named 'baz', within struct 'bar', within struct 'foo' -// tree.HasPath([]string{"foo","bar","baz"}) -// -// // returns the key at this path, if it is there -// tree.GetPath([]string{"foo","bar","baz"}) -// -// Note that this is distinct from the heavyweight query syntax supported by -// Tree.Query() and the Query() struct (see below). -// -// Position Support -// -// Each element within the Tree is stored with position metadata, which is -// invaluable for providing semantic feedback to a user. This helps in -// situations where the TOML file parses correctly, but contains data that is -// not correct for the application. In such cases, an error message can be -// generated that indicates the problem line and column number in the source -// TOML document. -// -// // load TOML data -// tree, _ := toml.Load("filename.toml") -// -// // get an entry and report an error if it's the wrong type -// element := tree.Get("foo") -// if value, ok := element.(int64); !ok { -// return fmt.Errorf("%v: Element 'foo' must be an integer", tree.GetPosition("foo")) -// } -// -// // report an error if an expected element is missing -// if !tree.Has("bar") { -// return fmt.Errorf("%v: Expected 'bar' element", tree.GetPosition("")) -// } +// Go-toml can operate on a TOML document as a tree. Use one of the Load* +// functions to parse TOML data and obtain a Tree instance, then one of its +// methods to manipulate the tree. // // JSONPath-like queries // diff --git a/doc_test.go b/doc_test.go index 31b4f40..9dd7738 100644 --- a/doc_test.go +++ b/doc_test.go @@ -6,7 +6,7 @@ import ( "fmt" ) -func Example_comprehensiveExample() { +func Example_tree() { config, err := LoadFile("config.toml") if err != nil { @@ -20,10 +20,34 @@ func Example_comprehensiveExample() { configTree := config.Get("postgres").(*Tree) user = configTree.Get("user").(string) password = configTree.Get("password").(string) - fmt.Println("User is ", user, ". Password is ", password) + fmt.Println("User is", user, " and password is", password) // show where elements are in the file fmt.Printf("User position: %v\n", configTree.GetPosition("user")) fmt.Printf("Password position: %v\n", configTree.GetPosition("password")) } } + +func Example_unmarshal() { + type Employer struct { + Name string + Phone string + } + type Person struct { + Name string + Age int64 + Employer Employer + } + + document := []byte(` + name = "John" + age = 30 + [employer] + name = "Company Inc." + phone = "+1 234 567 89012" + `) + + person := Person{} + Unmarshal(document, &person) + fmt.Println(person.Name, "is", person.Age, "and works at", person.Employer.Name) +} diff --git a/marshal.go b/marshal.go index 358425a..9bf6fb9 100644 --- a/marshal.go +++ b/marshal.go @@ -9,24 +9,6 @@ import ( "time" ) -/* -Tree structural types and corresponding marshal types -------------------------------------------------------------------------------- -*Tree (*)struct, (*)map[string]interface{} -[]*Tree (*)[](*)struct, (*)[](*)map[string]interface{} -[]interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{}) -interface{} (*)primitive - -Tree primitive types and corresponding marshal types ------------------------------------------------------------ -uint64 uint, uint8-uint64, pointers to same -int64 int, int8-uint64, pointers to same -float64 float32, float64, pointers to same -string string, pointers to same -bool bool, pointers to same -time.Time time.Time{}, pointers to same -*/ - type tomlOpts struct { name string include bool @@ -115,6 +97,22 @@ function for sub-structs, and currently only definite types can be marshaled Note that pointers are automatically assigned the "omitempty" option, as TOML explicity does not handle null values (saying instead the label should be dropped). + +Tree structural types and corresponding marshal types: + + *Tree (*)struct, (*)map[string]interface{} + []*Tree (*)[](*)struct, (*)[](*)map[string]interface{} + []interface{} (as interface{}) (*)[]primitive, (*)[]([]interface{}) + interface{} (*)primitive + +Tree primitive types and corresponding marshal types: + + uint64 uint, uint8-uint64, pointers to same + int64 int, int8-uint64, pointers to same + float64 float32, float64, pointers to same + string string, pointers to same + bool bool, pointers to same + time.Time time.Time{}, pointers to same */ func Marshal(v interface{}) ([]byte, error) { mtype := reflect.TypeOf(v) @@ -247,6 +245,8 @@ func (t *Tree) Unmarshal(v interface{}) error { // is no concept of an Unmarshaler interface or UnmarshalTOML function for // sub-structs, and currently only definite types can be unmarshaled to (i.e. no // `interface{}`). +// +// See Marshal() documentation for types mapping table. func Unmarshal(data []byte, v interface{}) error { t, err := LoadReader(bytes.NewReader(data)) if err != nil { diff --git a/marshal_test.go b/marshal_test.go index dbfc7c1..a3fa128 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -177,6 +177,25 @@ func TestDocUnmarshal(t *testing.T) { } } +func ExampleUnmarshal() { + type Postgres struct { + User string + Password string + } + type Config struct { + Postgres Postgres + } + + doc := []byte(` + [postgres] + user = "pelletier" + password = "mypassword"`) + + config := Config{} + Unmarshal(doc, &config) + fmt.Println("user=", config.Postgres.User) +} + func TestDocPartialUnmarshal(t *testing.T) { result := testDocSubs{} diff --git a/toml.go b/toml.go index 9966321..2d2f640 100644 --- a/toml.go +++ b/toml.go @@ -54,8 +54,7 @@ func (t *Tree) HasPath(keys []string) bool { return t.GetPath(keys) != nil } -// Keys returns the keys of the toplevel tree. -// Warning: this is a costly operation. +// Keys returns the keys of the toplevel tree (does not recurse). func (t *Tree) Keys() []string { keys := make([]string, len(t.values)) i := 0