Add directory-like indexing feature.
commit 28e155e57d39e10b53b203141469cf2811815644 · parent 79ab310c54
· browse this tree
Eric Myhre, 2021-08-18 14:57:36 UTC
diff --git a/index.go b/index.go new file mode 100644 index 0000000..25c554c --- /dev/null +++ b/index.go @@ -0,0 +1,41 @@ +package testmark + +import ( + "strings" +) + +// BuildDirIndex mutates the Document to set the DirEnt field. +// +// The order of ChildrenList in the DirEnt +// is determined by the order in which things are first seen in the Document's Hunk list. +// The "directories" can be implied, +// e.g. a Hunk with name="foo/bar" will cause the creation of a DirEnt with name "foo". +// +// No concept of path "cleaning" is applied. Paths like "." and ".." are not treated specially. +// A path containing repeated slashes is a fairly deranged thing to do, but also won't be rejected. +func (doc *Document) BuildDirIndex() { + doc.DirEnt = &DirEnt{} + for _, hunk := range doc.DataHunks { + doc.DirEnt.fill(strings.Split(hunk.Name, "/"), hunk.Hunk) + } +} + +func (dirent *DirEnt) fill(pathSegs []string, hunk Hunk) { + if len(pathSegs) == 0 { + dirent.Hunk = &hunk + return + } + if dirent.Children == nil { + dirent.Children = make(map[string]*DirEnt) + } + if next, exists := dirent.Children[pathSegs[0]]; exists { + next.fill(pathSegs[1:], hunk) + } else { + l := len(dirent.ChildrenList) + dirent.ChildrenList = append(dirent.ChildrenList, DirEnt{ + Name: pathSegs[0], + }) + dirent.Children[pathSegs[0]] = &dirent.ChildrenList[l] + dirent.ChildrenList[l].fill(pathSegs[1:], hunk) + } +} diff --git a/index_test.go b/index_test.go new file mode 100644 index 0000000..a4c9c19 --- /dev/null +++ b/index_test.go @@ -0,0 +1,39 @@ +package testmark + +import ( + "path/filepath" + "testing" +) + +func TestIndexingDirs(t *testing.T) { + testdata, err := filepath.Abs("testdata") + if err != nil { + panic(err) + } + doc, err := ReadFile(filepath.Join(testdata, "exampleWithDirs.md")) + doc.BuildDirIndex() + if len(doc.DirEnt.ChildrenList) != 2 { + t.Errorf("root dirent list should be length 2") + } + if len(doc.DirEnt.Children) != 2 { + t.Errorf("root dirent map should be length 2") + } + if doc.DirEnt.ChildrenList[0].Name != "one" { + t.Errorf("first child of root dirent should've been 'one'") + } + if doc.DirEnt.ChildrenList[1].Name != "really" { + t.Errorf("second child of root dirent should've been 'really'") + } + if doc.DirEnt.Children["one"].Hunk.Name != "one" { + t.Errorf("hunk 'one' looked up through dir maps should still have the right name") + } + if string(doc.DirEnt.Children["one"].Hunk.Body) != "baz\n" { + t.Errorf("hunk 'one' looked up through dir maps should have the right content") + } + if doc.DirEnt.Children["really"].Children["deep"].Children["dirs"].Children["wow"].Hunk.Name != "really/deep/dirs/wow" { + t.Errorf("hunk 'really/deep/dirs/wow' looked up through dir maps should still have the right name") + } + if string(doc.DirEnt.Children["really"].Children["deep"].Children["dirs"].Children["wow"].Hunk.Body) != "zot\n" { + t.Errorf("hunk 'really/deep/dirs/wow' looked up through dir maps should have the right content") + } +} diff --git a/testdata/exampleWithDirs.md b/testdata/exampleWithDirs.md new file mode 100644 index 0000000..92efb59 --- /dev/null +++ b/testdata/exampleWithDirs.md @@ -0,0 +1,42 @@ +an example with "directories" +============================= + +This testmark implementation provides convenience functions for treating +the data hunk names as if they were "paths" in some sort of "filesystem". + +So, in this file we'll exercise that by using a bunch of hunk names that have slashes in them. + +### directories can be implied + +[testmark]:# (one/two) +```text +foo +``` + +[testmark]:# (one/three) +```text +bar +``` + +### a path can have both children and data + +(Maybe avoid leaning on this, though, because some would argue it's weird.) + +[testmark]:# (one) +```text +baz +``` + +### directories can be implied deeply + +[testmark]:# (really/deep/dirs/wow) +```text +zot +``` + +### files in the same directory don't have to be subsequent + +[testmark]:# (one/four/bang) +```text +mop +``` diff --git a/testmark.go b/testmark.go index d6ad9eb..e570ff0 100644 --- a/testmark.go +++ b/testmark.go @@ -18,6 +18,11 @@ type Document struct { // Like it says on the tin. HunksByName map[string]DocHunk + + // An index over the hunks, which treats their names as if they were unix-style paths -- + // meaning they're split by slashes, and each segment is considered a directory. + // You must call the `BuildDirIndex()` function to cause this to be populated. + DirEnt *DirEnt } // DocHunk is the Document's internal idea of where hunks are. @@ -49,3 +54,18 @@ type Hunk struct { // (This is *still* a subslice of Document.Original, if this hunk was created by Parse, but probably a unique slice otherwise.) Body []byte } + +// DirEnt describes an index over hunks in a document that acts much like a filesystem. +// See Document.BuildDirIndex for details. +type DirEnt struct { + // The name of just this path segment. + // (Note that if there's a Hunk in this DirEnt, its name may be different -- it still has the *full* path name.) + Name string + + // A hunk, or nil. + Hunk *Hunk + + // Children, recursively. + Children map[string]*DirEnt + ChildrenList []DirEnt +}