package main import ( "bytes" "fmt" "git.giaever.org/joachimmg/go-ini.git/ini" "io" "strings" "time" ) type App struct { Name string Url string Scheme string } func m1() { // Set some data into the struct settings := &App{ Name: "My Application", Url: "localhost", Scheme: "https", } // Read struct and format to INI idata, err := ini.Marshal(settings) if err != nil { fmt.Println("An error occured", err) return } // Save here? No errors. fmt.Println(string(idata)) } func m2() { // Same as output from m1, // but we're now reading this and filling the struct idata := ` Name = My Application Url = localhost Scheme = https` settings := &App{} // Must make idata from string to []byte if err := ini.Unmarshal([]byte(idata), settings); err != nil { fmt.Println("An error occured", err) return } // User setting here. fmt.Println(settings, settings.Name) } // AccessLevel type (easier to transform into text) type AccessLevel int // Make some access levels const ( Admin AccessLevel = 1 << iota User Guest Unknown ) // String returns the text representation func (a AccessLevel) String() string { switch a { case Admin: return "ADMIN" case User: return "USER" case Guest: return "GUEST" default: return "UNKNOWN" } } // We must be able to restore the numeric value func (a *AccessLevel) SetAccessLevel(al string) { for i := 1; AccessLevel(i) != Unknown; i = i << 1 { if AccessLevel(i).String() == al { *a = AccessLevel(i) return } } *a = Unknown } // Person defines an user type Person struct { Name string Birth ini.Date Access AccessLevel } // MarshalINI to get the ini-format of one person func (p *Person) MarshalINI() ([]byte, error) { // Uses ini.Date own marshaller method (see: types.go) b, err := p.Birth.MarshalINI() if err != nil { return nil, err } // Format: ||, e.g my name|1985-09-85-UTC-0 return []byte(fmt.Sprintf("%s|%s|%s", string(p.Name), string(b), p.Access.String())), nil } // UnmarshalINI reads the format produces by MarshalINI // and creates the person/user again func (p *Person) UnmarshalINI(b []byte) error { // Split ||(see MarshalINI above for format) spl := strings.Split(string(b), "|") // Do some form of validation (remember: this is just an example!) if len(spl) != 3 { return fmt.Errorf("Invalid format. Expected |. Got %s", string(b)) } // Set name p.Name = spl[0] // Uses trimspace here to ensure that the trailing \n (newline) is removed // as this is last item in || p.Access.SetAccessLevel(strings.TrimSpace(spl[2])) // And use ini.Date's own unmarshaller method (see: types.go) return p.Birth.UnmarshalINI([]byte(spl[1])) } // Persons is several persons type Persons []Person // Personell is users with given access levels type Personell struct { Users Persons } func (p *Persons) MarshalINI() ([]byte, error) { // []string to store each person in var persons []string for i := 0; i < len(*p); i++ { // Run marshaller method on each (see Person.Marshal() above) person, err := (*p)[i].MarshalINI() // Ensure there arent any errors... if err != nil { return nil, err } // Add the person visual representation to the list of persons persons = append(persons, string(person)) } // Return as a multiline ini-entry return []byte(strings.Join(persons, "\\\n")), nil } // This is left for the reader to solve ;) func (p *Persons) UnmarshalINI(b []byte) error { persons := bytes.NewBuffer(b) for pbytes, err := persons.ReadBytes('\n'); err == nil; pbytes, err = persons.ReadBytes('\n') { var person Person person.UnmarshalINI(pbytes) *p = append(*p, person) if err == io.EOF { break } } return nil } func m3() { p := &Personell{} // Fill some data! p.Users = append(p.Users, Person{ Name: "Testing Johanson", Birth: ini.Date{time.Date(1985, time.Month(9), 23, 0, 0, 0, 0, time.UTC)}, Access: Admin, // Admin }, Person{ Name: "Testing Aniston", Birth: ini.Date{time.Date(1987, time.Month(1), 12, 0, 0, 0, 0, time.UTC)}, Access: User, // Unknown user }, Person{ Name: "Testing Cubain", Birth: ini.Date{time.Date(1986, time.Month(3), 3, 0, 0, 0, 0, time.UTC)}, Access: Guest, // Guest }, ) idata, err := ini.Marshal(p) if err != nil { fmt.Println(err) return } fmt.Println(string(idata)) } func m4() { // Output from m3 idata := ` Users = Testing Johanson|1985-9-23-UTC-0|ADMIN\ Testing Aniston|1987-1-12-UTC-0|USER\ Testing Cubain|1986-3-3-UTC-0|GUEST` p := &Personell{} if err := ini.Unmarshal([]byte(idata), p); err != nil { fmt.Println(err) return } fmt.Println("Success, you did it!", p) } func main() { m1() m2() m3() m4() } /** * DEBUG DATA **/ type Test struct { Str string `ini:"String"` UnquotedString string MultipleLines string WithoutValue bool Date ini.Date Time ini.Time TestTime time.Time IP ini.IP Int struct { Int int Int8 int8 Int16 int16 Int32 int32 Int64 int64 Uint struct { Uint uint Uint8 uint8 Uint16 uint16 Uint32 uint32 Uint64 uint64 } } Float struct { Float32 float32 Float64 float64 } } func (t *Test) String() string { return fmt.Sprintf(` STRUCT DATA: Str: %v UnquotedString: %v MultipleLines: %v WithoutValue: %v Date: %v Time: %v TestTime: %v IP: %v Int { Int: %v Int8: %v Int16: %v Int32: %v Int64: %v Uint { Uint: %v Uint8: %v Uint16: %v Uint32: %v Uint64: %v } } Float { Float32: %v Float64: %v }`, t.Str, t.UnquotedString, strings.Replace(t.MultipleLines, "\n", "\n\t\t\t\t", -1), t.WithoutValue, t.Date, t.Time, t.TestTime, t.IP, t.Int.Int, t.Int.Int8, t.Int.Int16, t.Int.Int32, t.Int.Int64, t.Int.Uint.Uint, t.Int.Uint.Uint8, t.Int.Uint.Uint16, t.Int.Uint.Uint32, t.Int.Uint.Uint64, t.Float.Float32, t.Float.Float64, ) } func sep(f, e string) { var s []byte for i := 0; i < 80; i++ { s = append(s, '/') } fmt.Println(f, string(s), e) } func debug() { text := ` String = "This is some text" UnquotedString = This is some unquoted text MultipleLines = This is \ multiple lines \ going on WithoutValue Date = 2017-9-28-Local-0 Time = 01 Jan 01 00:00 UTC IP = :: [Int] Int = -1 Int8 = -8 Int16 = -16 Int32 = -32 Int64 = -64 [Int.Uint] Uint = 1 Uint8 = 8 Uint16 = 16 Uint32 = 32 Uint64 = 64 Uint64 = 64 [Float] Float32 = 3.14722354762387648273648726348 Float64 = 10.00003423472342734897234872347293748` first := &Test{} sep("", "") fmt.Println(" ### INI TO UNMARSHAL:") sep("", "") fmt.Println(text) sep("\n", "") fmt.Println(" ### INITIAL STRUCT VALUES (empty/unset values):") sep("", "") fmt.Println(first) sep("\n", "") fmt.Println(" --- NOW MARSHALING: INI -> STRUCT ---") err := ini.Unmarshal([]byte(text), first) fmt.Println(" !!! ERROR ON UMARSHALING:", err) sep("", "") fmt.Println(" ### NEW STRUCT VALUES (unmarshaled from INI):") sep("", "\n") fmt.Println(first) sep("\n", "") fmt.Println(" --- NOW UNMARSHALING: STRUCT -> INI ---") unm, err := ini.Marshal(first) fmt.Println(" !!! ERROR ON MARCHALING:", err) sep("", "") fmt.Println(" ### PRODUCED INI (marshaled from STRUCT)") sep("", "") fmt.Println(string(unm)) sep("", "") }