Heading 2 Recipes class mt-12 Heading 3 Parse object properties class mb-2 Markdown Read known properties from a Terrace block and write them to a struct. CodeBlock go package main import ( "fmt" "io" "strconv" "strings" "terrace.go" ) type Config struct { StringProperty string NumericProperty int } func main() { input := `object string_property An example string numeric_property 42` reader := &StringReader{reader: strings.NewReader(input)} doc := terrace.NewTerraceDocument(reader, ' ') config := Config{} for { node, err := doc.Next() if err == io.EOF { break } if err != nil { panic(err) } if node.Head() == "object" { objectLevel := node.Level() for { node, err := doc.Next() if err == io.EOF { break } if err != nil { panic(err) } if node.Level() <= objectLevel { // We've exited the object block break } switch node.Head() { case "string_property": config.StringProperty = node.Tail() case "numeric_property": if val, err := strconv.Atoi(node.Tail()); err == nil { config.NumericProperty = val } } } } } fmt.Printf("%+v\n", config) // {StringProperty:An example string NumericProperty:42} } Markdown Read *all* properties as strings from a Terrace block and write them to a map. CodeBlock go package main import ( "fmt" "io" "strings" "terrace.go" ) func main() { input := `object property1 Value 1 property2 Value 2 random_property igazi3ii4quaC5OdoB5quohnah1beeNg` reader := &StringReader{reader: strings.NewReader(input)} doc := terrace.NewTerraceDocument(reader, ' ') output := make(map[string]string) for { node, err := doc.Next() if err == io.EOF { break } if err != nil { panic(err) } if node.Head() == "object" { objectLevel := node.Level() for { node, err := doc.Next() if err == io.EOF { break } if err != nil { panic(err) } if node.Level() <= objectLevel { // We've exited the object block break } // Skip empty lines if node.Content() == "" { continue } // Add any properties to the map as strings using the // line Head() as the key and Tail() as the value output[node.Head()] = node.Tail() } } } fmt.Printf("%+v\n", output) // map[property1:Value 1 property2:Value 2 random_property:igazi3ii4quaC5OdoB5quohnah1beeNg] } Heading 3 Process nested blocks class mb-2 Markdown Handle hierarchically nested blocks with recursive processing. CodeBlock go package main import ( "fmt" "io" "strings" "terrace.go" ) type Block struct { Name string Content string Children []Block } func parseBlock(doc *terrace.TerraceDocument, parentLevel int) []Block { var blocks []Block for { node, err := doc.Next() if err == io.EOF { break } if err != nil { panic(err) } // If we've returned to the parent level or higher, we're done if node.Level() <= parentLevel { break } block := Block{ Name: node.Head(), Content: node.Tail(), } // Parse any nested children block.Children = parseBlock(doc, node.Level()) blocks = append(blocks, block) } return blocks } func main() { input := `root section1 Section 1 Content subsection1 Subsection 1 Content subsection2 Subsection 2 Content section2 Section 2 Content nested deeply Nested Content` reader := &StringReader{reader: strings.NewReader(input)} doc := terrace.NewTerraceDocument(reader, ' ') blocks := parseBlock(doc, -1) fmt.Printf("%+v\n", blocks) }