orderednodes

package module
v0.0.0-...-01c6991 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 3, 2025 License: MIT Imports: 8 Imported by: 4

README

orderednodes

Nodes with ordered children, to use for XML mixed content and with golang os.DirFS

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

View Source
const (
	Sep     = os.PathSeparator
	ListSep = os.PathListSeparator
)

Variables

View Source
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.

View Source
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

type FilePropsNord struct {
	Nord
	FU.FSItem
}

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 InspectorFunc func(pNode Norder) error

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

func NewNord(aRelPath string) *Nord

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) AbsFP

func (p *Nord) AbsFP() string

AbsFP is duh.

func (*Nord) AddKid

func (p *Nord) AddKid(aKid Norder) Norder

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

func (p *Nord) AddKids(rKids []Norder) Norder

AddKids adds the supplied nodes as kids, after any pre-existing kids, and returns the parent.

func (*Nord) Echo

func (p *Nord) Echo() string

Echo implements Markupper.

func (*Nord) FirstKid

func (p *Nord) FirstKid() Norder

FirstKid provides read-only access for other packages. Can return nil.

func (*Nord) HasKids

func (p *Nord) HasKids() bool

HasKids is duh.

func (*Nord) IsDir

func (p *Nord) IsDir() bool

IsDir does NOT work, because we are not setting bool isDir yet. It is set (or not set) in embedding structs.

func (*Nord) IsRoot

func (p *Nord) IsRoot() bool

IsRoot is duh.

func (*Nord) KidsAsSlice

func (p *Nord) KidsAsSlice() []Norder

func (*Nord) LastKid

func (p *Nord) LastKid() Norder

LastKid provides read-only access for other packages. Can return nil.

func (*Nord) Level

func (p *Nord) Level() int

Level is duh.

func (Nord) LinePrefixString

func (p Nord) LinePrefixString() string

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 (p *Nord) LineSummaryString() string

func (*Nord) NextKid

func (p *Nord) NextKid() Norder

NextKid provides read-only access for other packages. Can return nil.

func (*Nord) Parent

func (p *Nord) Parent() Norder

Parent returns the parent, duh.

func (*Nord) PrevKid

func (p *Nord) PrevKid() Norder

PrevKid provides read-only access for other packages. Can return nil.

func (*Nord) PrintCssTree

func (p *Nord) PrintCssTree(w io.Writer) error

func (*Nord) PrintTree

func (p *Nord) PrintTree(w io.Writer) error

func (*Nord) RelFP

func (p *Nord) RelFP() string

RelFP is duh.

func (*Nord) ReplaceWith

func (pOld *Nord) ReplaceWith(pNew Norder) Norder

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.

func (*Nord) SetFirstKid

func (p *Nord) SetFirstKid(p2 Norder)

SetFirstKid has no side effects.

func (*Nord) SetLastKid

func (p *Nord) SetLastKid(p2 Norder)

SetLastKid has no side effects.

func (*Nord) SetNextKid

func (p *Nord) SetNextKid(p2 Norder)

SetNextKid has no side effects.

func (*Nord) SetParent

func (p *Nord) SetParent(p2 Norder)

SetParent has no side effects.

func (*Nord) SetPrevKid

func (p *Nord) SetPrevKid(p2 Norder)

SetPrevKid has no side effects.

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 ReplacerFunc func(Norder) Norder

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

type StringFunc func(Norder) string

StringFunc is used by interface Norder, so its method signature actually (MAYBE!) looks like: func (*Nord) FuncName() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL