package main import ( "encoding/json" "io/ioutil" "log" "path/filepath" "regexp" "strconv" "github.com/bouncepaw/mycorrhiza/cfg" ) const ( hyphaPattern = `[^\s\d:/?&\\][^:?&\\]*` hyphaUrl = `/{hypha:` + hyphaPattern + `}` revisionPattern = `[\d]+` revQuery = `{rev:` + revisionPattern + `}` metaJsonPattern = `meta\.json` ) var ( leadingInt = regexp.MustCompile(`^[-+]?\d+`) ) // matchNameToEverything matches `name` to all filename patterns and returns 4 boolean results. func matchNameToEverything(name string) (metaJsonM, hyphaM bool) { // simpleMatch reduces boilerplate. Errors are ignored because I trust my regex skills. simpleMatch := func(s string, p string) bool { m, _ := regexp.MatchString(p, s) return m } return simpleMatch(name, metaJsonPattern), simpleMatch(name, hyphaPattern) } // scanHyphaDir scans directory at `fullPath` and tells what it has found. func scanHyphaDir(fullPath string) (valid bool, possibleSubhyphae []string, metaJsonPath string, err error) { nodes, err := ioutil.ReadDir(fullPath) if err != nil { return // implicit return values } for _, node := range nodes { metaJsonM, hyphaM := matchNameToEverything(node.Name()) switch { case hyphaM && node.IsDir(): possibleSubhyphae = append(possibleSubhyphae, filepath.Join(fullPath, node.Name())) case metaJsonM && !node.IsDir(): metaJsonPath = filepath.Join(fullPath, "meta.json") // Other nodes are ignored. It is not promised they will be ignored in future versions } } if metaJsonPath != "" { valid = true } return // implicit return values } // hyphaName gets name of a hypha by stripping path to the hypha in `fullPath` func hyphaName(fullPath string) string { // {cfg.WikiDir}/{the name} return fullPath[len(cfg.WikiDir)+1:] } // recurFindHyphae recursively searches for hyphae in passed directory path. func recurFindHyphae(fullPath string) map[string]*Hypha { hyphae := make(map[string]*Hypha) valid, possibleSubhyphae, metaJsonPath, err := scanHyphaDir(fullPath) if err != nil { return hyphae } // First, let's process subhyphae for _, possibleSubhypha := range possibleSubhyphae { for k, v := range recurFindHyphae(possibleSubhypha) { hyphae[k] = v } } // This folder is not a hypha itself, nothing to do here if !valid { return hyphae } // Template hypha struct. Other fields are default json values. h := Hypha{ FullName: hyphaName(fullPath), Path: fullPath, parentName: filepath.Dir(hyphaName(fullPath)), // Children names are unknown now } metaJsonContents, err := ioutil.ReadFile(metaJsonPath) if err != nil { log.Printf("Error when reading `%s`; skipping", metaJsonPath) return hyphae } err = json.Unmarshal(metaJsonContents, &h) if err != nil { log.Printf("Error when unmarshaling `%s`; skipping", metaJsonPath) log.Println(err) return hyphae } // fill in rooted paths to content files and full names for idStr, rev := range h.Revisions { rev.FullName = filepath.Join(h.parentName, rev.ShortName) rev.Id, _ = strconv.Atoi(idStr) if rev.BinaryName != "" { rev.BinaryPath = filepath.Join(fullPath, rev.BinaryName) } rev.TextPath = filepath.Join(fullPath, rev.TextName) } // Now the hypha should be ok, gotta send structs hyphae[h.FullName] = &h return hyphae }