package main import ( "fmt" "strings" ) var ErrPageNotFound = fmt.Errorf("page not found") // A Page is a single unit of content on the site. type Page struct { Title string Contents []byte } // Index is a set of pages that exist at this level, // as well as a set of "folders" that contain sub-indexes. // The 'index' key in Pages is special, and will be returned // if no key is provided. type Index struct { Children map[string]Index Pages map[string]Page } // Page returns the requested page from the index, recursively // key is assumed to be a `/`-separated string; Page will split on the slashes, // descending into an index to find the page, if possible. If no match is found, // return an empty page and `ErrPageNotFound`. // `foo/` will return a page named `foo` in the current index, if it exists. // Otherwise, if a child index named "foo" exists, page will attempt to return its index page. // Page strips leading / from keys. func (i *Index) Page(key string) (*Page, error) { if key == "" || key == "/" { key = "index" } if key[0] == '/' { // strip leading slash key = key[1:] } curr, rest, found := strings.Cut(key, "/") page, pageok := i.Pages[curr] child, childok := i.Children[curr] // no trailing slash and doesn't exist if !found && !pageok { return &Page{}, ErrPageNotFound } // trailing slash, exists as page if rest == "" && pageok { return &page, nil } // neither page nor child if !childok { return &Page{}, ErrPageNotFound } // recurse return (&child).Page(rest) } // Save stores a page in the index, recursively, // overwriting any that may have existed before. // `foo/` is stored as a page named 'foo' in the current index; // default 'index' files should be explicitly passed as such. // The empty key or `/` are invalid and result in an error. // Leading slashes are stripped. func (i *Index) Save(key string, page *Page) error { if key == "" || key == "/" { return fmt.Errorf("invalid page key") } if key[0] == '/' { // strip leading slash key = key[1:] } // init maps if not yet created if i.Pages == nil { i.Pages = map[string]Page{} } if i.Children == nil { i.Children = map[string]Index{} } // save as page curr, rest, _ := strings.Cut(key, "/") if rest == "" { i.Pages[curr] = *page return nil } // recurse and save in child children := i.Children[curr] err := (&children).Save(rest, page) if err != nil { return err } i.Children[curr] = children return nil }