Documentation
¶
Overview ¶
Package orderednodes is a way to create a hierarchical tree of nodes, where the nodes are ordered and keep their order, and without needing or using Go generics.
Node order is of course important for markup in general and XML mixed content in particular, while unimportant when XML is used purely for data records.
interface Norder is implemented not for type Nord but rather for `*Nord´. NThis is so that nodes are writable.
Index ¶
- Constants
- Variables
- func InspectTree(p Norder, f InspectorFunc) error
- func InspectTreeWithPreAndPost(p Norder, f0 InspectorFunc, f1 InspectorFunc) error
- func WalkNorderTree(root Norder, fn fs.WalkDirFunc) error
- type FilePropsNord
- type InspectorFunc
- type Nord
- func (p *Nord) AbsFP() string
- func (p *Nord) AddKid(aKid Norder) Norder
- func (p *Nord) AddKids(rKids []Norder) Norder
- func (p *Nord) Echo() string
- func (p *Nord) FirstKid() Norder
- func (p *Nord) HasKids() bool
- func (p *Nord) IsDir() bool
- func (p *Nord) IsRoot() bool
- func (p *Nord) KidsAsSlice() []Norder
- func (p *Nord) LastKid() Norder
- func (p *Nord) Level() int
- func (p Nord) LinePrefixString() string
- func (p *Nord) LineSummaryFunc() StringFunc
- func (p *Nord) LineSummaryString() string
- func (p *Nord) NextKid() Norder
- func (p *Nord) Parent() Norder
- func (p *Nord) PrevKid() Norder
- func (p *Nord) PrintCssTree(w io.Writer) error
- func (p *Nord) PrintTree(w io.Writer) error
- func (p *Nord) RelFP() string
- func (pOld *Nord) ReplaceWith(pNew Norder) Norder
- func (p *Nord) Root() RootNorder
- func (p *Nord) SetFirstKid(p2 Norder)
- func (p *Nord) SetLastKid(p2 Norder)
- func (p *Nord) SetNextKid(p2 Norder)
- func (p *Nord) SetParent(p2 Norder)
- func (p *Nord) SetPrevKid(p2 Norder)
- type NordEngine
- type Norder
- type ReplacerFunc
- type RootFilePropsNord
- type RootNord
- type RootNorder
- type StringFunc
Constants ¶
const ( Sep = os.PathSeparator ListSep = os.PathListSeparator )
Variables ¶
var SkipAll error = fs.SkipAll
SkipAll as a return value (from [NordProcFunc]) says skip all remaining files and directories. It is not returned as an error by any function.
var SkipDir error = fs.SkipDir
SkipDir as a return value (from [NordProcFunc]) says to skip the directory named in the call. It is not returned as an error by any function.
Functions ¶
func InspectTree ¶
func InspectTree(p Norder, f InspectorFunc) error
func InspectTree used to be func WalkNorders .
func InspectTreeWithPreAndPost ¶
func InspectTreeWithPreAndPost(p Norder, f0 InspectorFunc, f1 InspectorFunc) error
func WalkNorderTree ¶
func WalkNorderTree(root Norder, fn fs.WalkDirFunc) error
WalkNorderTree walks the tree of [ON.Norder]s, calling `NorderProcFunc` for each Norder in the tree, including the input Norder.
?? All errors that arise visiting files and directories are filtered by fn: ?? see the fs.WalkDirFunc documentation for details.
The paths associated with the `Norder`s are, at each level downward, pretty much assumed to be in lexical order. .
Types ¶
type FilePropsNord ¶
FilePropsNord is an Ordered Propertied Path node: NOT ONLY the child nodes have a specific specified order BUT ALSO each node has a filepath plus the file item itself including its contents (in TypedRaw). This means Pthat every Parent node is a directory.
It also means we can use the redundancy to do a lot of error checking. Also we can use fields of seqId's to store parent and kid seqId's, adding yet another layer of error checking and simplified access.
type InspectorFunc ¶
type Nord ¶
type Nord struct {
// contains filtered or unexported fields
}
Nord is shorthand for "ordered node" (or "ordinal node") - a Node with ordered children nodes: the child nodes have a specific specified order. Ordering is essential for representation of content.
(It can help in other contexts too, such as filesystem operations, but it turns out that the Go stdlib generally returns directory items in lexical order and walks directories in lexical order.)
The ordering lets us define funcs like FirstKid, NextKid, PrevKid, LastKid. They are defined in interface Norder. A Nord for file/dir also contains its own relative path (relative to its inbatch) and absolute path.
Alternatives Everywhere ¶
There are three use cases identified for Nords:
- files & directories (here ordering is less important)
- XML/HTML/DOM markup (this creates problems handling same-named siblings)
- [Lw]DITA map files and other ToC's (these should be an ideal use case)
Also there are two distinct memory management nodes for allocating and linking nodes:
- The "traditional method" of allocating nodes individually, and linking them using pointers. Using this method, both deletions and insertions are relatively simple.
- Such nodes can also be loaded into a map, for random access to nodes based on path.
- The "new-fangled way" called an "arena", where we put all our nodes in a big slice, and link them using indices. This method is much kinder on memory management, but might becomes clumsy when we need dynamic node management.
- Deletions are easy if we just zero out the slice entry; we cannot then do compaction because it would require updating all indices past the first point of compaction.
- Insertions are costly. However note that in this implementation, for a node's kids, we use a linked list rather than a slice, so this makes it easier to append a new node at the end of the arena-slice and then update indices, wherever they may be elsewhere in the slice.
- In any case, if an arena-slice has to grow (because of a call to append), it might be moved elsewhere in memory, which would invalidate all ptrs to other Nords! If this happens, we should trust only the "traditional method".
Also there are multiple ways to represent node trees in our SQLite DBMS, and multiple ways to walk a node tree, so there is a unavoidable complexity wherever we look.
NOTE: DOM markup exhibits name duplication: we never have two same-named files in the same directory, but we might (e.g.) have multiple sibling <p> tags. So when representing markup, a map from paths to Nords would fail unless the tags are made unique with subscript indices, such as "[1]", "[2]", like those used in (e.g.) JQuery.
NOTE: Using Nords for files & dirs exhibits strong typing. Dirs are dirs and files are files and never the twain shall meet. This means that dirs cannot contain own-content and that files can never be non-leaf nodes. (Note tho that symlinks have aspects of both.) However this dir/file/etc typing is too complex to handle here in a Nord, so it is handled instead by a struct type that embeds Nord, such as [fileutils.FSItem].
If we build up a tree of Nords when processing an os.DirFS, the strict ordering provided by DirFS is not strictly needed, BUT it can anyways be used (and relied upon) because the three flavors of WalkDir are deterministic (using lexical order). It means that a given Nord will always appear AFTER the Nord for its directory has appeared, which makes it much easier to build a tree.
Link fields are lower-cased so that other packages cannot damage links.
NOTE: This implementation stores pointers to child nodes in a doubly linked list, not a slice, and a Nord does not have a complete set of pointers to all of its kids. Therefore (a) it is not simple to get a kid count, because it requires a list traversal, and (b) it is not feasible to use this same code to define a simpler, more efficient variant of Nord that has unordered kids. .
func NewNord ¶
NewNord expects a relative path (!!), and does not either (a) set/unset the bool [isDir] or (b) load file content, because these are expensive operations that can and should be done elsewhere, and also (c) they do not apply if this is being used for XML DOM.
func NewRootNord ¶
func NewRootNord(rootPath string, smryFunc StringFunc) *Nord
NewRootNord verifies it got a directory, and then sets the bools [isRoot] and [isDir]. Note that the passed-in field [rootPath] is set elsewhere, and must be set in the global NordEng before any child Nord is created using NewNord.
func (*Nord) AddKid ¶
AddKid adds the supplied node as the last kid, and returns it (i.e. the new last kid), now linked into the tree.
func (*Nord) AddKids ¶
AddKids adds the supplied nodes as kids, after any pre-existing kids, and returns the parent.
func (*Nord) IsDir ¶
IsDir does NOT work, because we are not setting bool isDir yet. It is set (or not set) in embedding structs.
func (*Nord) KidsAsSlice ¶
func (Nord) LinePrefixString ¶
LinePrefixString provides indentation and should start a line of display/debug.
It does not end the string with (white)space. .
func (*Nord) LineSummaryFunc ¶
func (p *Nord) LineSummaryFunc() StringFunc
func (*Nord) LineSummaryString ¶
func (*Nord) ReplaceWith ¶
AddKid adds the supplied node as the last kid, and returns it (i.e. the new last kid), now linked into the tree.
func (*Nord) Root ¶
func (p *Nord) Root() RootNorder
Root walks the tree upward until [IsRoot] is true, so it does not use any global variables.
type NordEngine ¶
type NordEngine struct {
// contains filtered or unexported fields
}
NordEngine tracks the state of a Nord tree being assembled, for example when a directory is specified for recursive analysis.
var NordEng *NordEngine = new(NordEngine)
NordEng is a package global, which is dodgy and not re-entrant. The solution probably involves currying.
type Norder ¶
type Norder interface {
// SeqID() int
// SetSeqID(int)
// Level is zero-based (i.e. root nord's is 0)
Level() int
// RelFP is rel.filepath for a file/dir, and for a DOM
// node, it is meaningless, unless it is a [RootNorder],
// for which it is the rel.path to the DOCUMENT
RelFP() string
// AbsFP is abs.filepath for a file/dir, and for
// a DOM node, the (abs.)path of the node w.r.t.
// the document root, except for a [RootNorder],
// for which it is the abs.path to the DOCUMENT
AbsFP() string
IsRoot() bool
// Root should always return the root, at arena index 0
Root() RootNorder
IsDir() bool
// IsDirlike() bool // FIXME: add this!
Parent() Norder
HasKids() bool
FirstKid() Norder
LastKid() Norder
PrevKid() Norder
NextKid() Norder
KidsAsSlice() []Norder
// AddKid returns the kid, who knows his
// own arena index (using [slices.Index])
AddKid(Norder) Norder
// AddKids returns the method target
// - the parent of all the kids
AddKids([]Norder) Norder
ReplaceWith(Norder) Norder
SetParent(Norder)
SetPrevKid(Norder)
SetNextKid(Norder)
SetFirstKid(Norder)
SetLastKid(Norder)
LinePrefixString() string
LineSummaryString() string
LineSummaryFunc() StringFunc
PrintTree(io.Writer) error
// contains filtered or unexported methods
}
Norder is satisfied by *Nord NOT by Nord.
func ReplaceTree ¶
func ReplaceTree(oldRoot Norder, f ReplacerFunc) (newRoot Norder, err error)
type ReplacerFunc ¶
type RootFilePropsNord ¶
type RootFilePropsNord FilePropsNord
Available to ensure that assignments to/from root node are explicit.
type RootNord ¶
type RootNord Nord
RootNord is defined, so that assignments to/from a root node have to be explicit.
type RootNorder ¶
type RootNorder interface {
Norder
}
RootNorder is kind of TBD, but it is (in principle) satisfied by *RootNord and/but NOT by RootNord.
type StringFunc ¶
StringFunc is used by interface Norder, so its method signature actually (MAYBE!) looks like: func (*Nord) FuncName() string