menuvalidator/main.go (83 lines of code) (raw):

package main import ( "encoding/json" "flag" "fmt" "io/ioutil" "net/url" "os" ) type MenuEntry struct { Type string `json:"type"` Text string `json:"text"` Href string `json:"href"` AdminOnly bool `json:"adminOnly"` Content []MenuEntry `json:"content"` } /** read, parse and unmarshal the json. Exits the program with an error message if the data is not valid. */ func read_data(filename string) []MenuEntry { file, openErr := os.Open(filename) if openErr != nil { fmt.Printf("Could not open file '%s': %s\n", filename, openErr) os.Exit(1) } defer file.Close() rawContent, readErr := ioutil.ReadAll(file) if readErr != nil { fmt.Printf("Could not read file '%s': %s\n", filename, readErr) os.Exit(1) } var entries []MenuEntry marshalErr := json.Unmarshal(rawContent, &entries) if marshalErr != nil { fmt.Printf("Menu from '%s' is not valid: %s\n", filename, marshalErr) os.Exit(1) } return entries } /** performs a deep validation of a list of menu entries. Any that contain sub-levels are then recursively validated by the same function. :param entries: a pointer a a list of MenuEntry to validate :param level: an integer representing the recursion level. Call this with 0 to start. :returns: a boolean indicating True for failure or False for success */ func recursivelyValidate(entries *[]MenuEntry, level int) bool { did_fail := false for i, entry := range *entries { if entry.Text == "" { fmt.Printf("Menu entry %d at sublevel %d has no menu text\n", i, level) did_fail = true } if entry.Type != "link" && entry.Type != "submenu" { fmt.Printf("Menu entry %d at level %d (%s) has an invalid type %s - expecting 'link' or 'submenu'\n", i, level, entry.Text, entry.Type) did_fail = true } if entry.Href == "" && entry.Content == nil { fmt.Printf("Menu entry %d at level %d (%s %s) has blank href and content\n", i, level, entry.Type, entry.Text) did_fail = true } if entry.Href != "" && entry.Content != nil { fmt.Printf("Menu entry %d at level %d (%s %s) has both href and content set\n", i, level, entry.Type, entry.Text) did_fail = true } if entry.Href != "" { _, urlErr := url.Parse(entry.Href) if urlErr != nil { fmt.Printf("Link for menu entry %d at level %d (%s %s) is not valid: %s\n", i, level, entry.Type, entry.Text, urlErr) did_fail = true } } if entry.Content != nil && len(entry.Content) > 0 { subLevelFailed := recursivelyValidate(&entry.Content, level+1) if subLevelFailed { did_fail = true } } } return did_fail } func main() { filename := flag.String("file", "/etc/menu.json", "the menu.json file to validate") flag.Parse() entries := read_data(*filename) //carry out the _whole_ validation and only exit at the end so we can see all the errors did_fail := recursivelyValidate(&entries, 0) if did_fail { fmt.Printf("Validation of %s failed, see above errors for details\n", *filename) os.Exit(1) } fmt.Printf("Validation of %s succeeded!\n", *filename) os.Exit(0) }