nork

package module
v0.0.0-...-6ce6979 Latest Latest
Warning

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

Go to latest
Published: May 19, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package nork is Nodes with ORdered Kids - a way to create a hierarchical tree of nodes, where the nodes are ordered and keep their order. It does not need or use Go generics.

Node order is of course important for markup in general and XML mixedcontent in particular, while unimportant when XML is used purely for data records.

interface Norker is implemented not for type Nork but rather for `*Nork´. This is so that nodes are shared-writable.

nork is a Node with Ordered Kids. It stores basic bidirectional parent/kid relationships, plus other useful hierarchy-related info like levels and paths.

interface Norker is implemented not for struct Nork but rather for the pointer, i.e. `*Nork´. This makes nodes writable and also sharable.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DebugG

func DebugG(p *Nork) string

func EchoG

func EchoG(p *Nork) string

func Example

func Example()

func InfosG

func InfosG(p *Nork) string

func NewFSOTreeNorkFactory

func NewFSOTreeNorkFactory(aRootPath string) (
	*FSOTreeNorkFactory, *FSONork, error)

NewFSOTreeNorkFactory works with a directory in a filesystem:

  • it verifies that it got a directory
  • it also creates and returns the root Nork
  • an error return is a *os.PathError

If the target is a single file (instead of a directory), just use func NewFSOLoneNork, which is also used to create the initial root Nork in this func.

TBD: Global index counter or ask SQLite ? TBD: Maybe also pass in a "NewNork" function. .

Types

type Cntyer

type Cntyer interface {
	// =========================
	//  FILEPATHS (Rel. & Abs.)
	// =========================
	// RelFP is rel.filepath for a file/dir.
	RelFP() string
	SetRelFP(string)
	// AbsFP is abs.filepath for a file/dir.
	AbsFP() string
	SetAbsFP(string)
	// Kids stuff that only makes sense for Kids in a linked list.
	SetFirstKid(*Nork)
	SetLastKid(*Nork)
	SetPrevPeer(*Nork)
	SetNextPeer(*Nork)
}

Cntyer is satisfied by [*Cnty] NOT by Cnty.

type FSONork

type FSONork struct {
	Nork
	// FSO includes paths and flags like IsDir and DoesNotExist.
	// (For non-FS use case, IsDir() might be sorta "CanKids".)
	// (IsDirlike should be considered TBD for anything but FS.)
	FSO *FU.FSObject
	// contains filtered or unexported fields
}

FSONork embeds an FSO and a Nork (a "QuadraNode").

The UC (Use Case) is:

  • UC.FSI: File System Item: represent a dir-or-file-or-softlink: Here ordering is less important. Paths are valid FS paths. Prnt:dir, Kids:contents(!dir-v-file), Usrs:incoming-symlinks

Using Norks for files & dirs exhibits strong typing. Dirs are dirs and files are files and never the twain shall meet. This means that (a) dirs cannot contain own-content, and (b) 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 Nork, because a leaf node can be either a file or a dir, and a field like "canKid bool" is a bit OTT, so the file/dir distinction is handled instead by an outer struct type that embeds Nork, such as FSONork, embedding [fileutils.FSObject].

If we build up a tree of Norks 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). WalkDir does tho promise that a given Nork will always appear AFTER the Nork for its directory has appeared, which makes it "easy" to build a tree. .

func NewFSOLoneNork

func NewFSOLoneNork(aRelPath string) *FSONork

NewFSOLoneNork prefers a relative path. It does not load file content, which is an expensive operation done elsewhere. But it loads the [Filepaths], which does checks specific to filesystem objects. Then the content can be loaded lazily at any time.

Do NOT use this for a path that is not a filesystem path. .

func (*FSONork) AbsFP

func (p *FSONork) AbsFP() string

AbsFP is an absolute filepath. Discussion: It is the same as rel.path, but expanded to be rooted in - i.e. traced back to the root of - a filesystem.

func (*FSONork) IsDirlike

func (p *FSONork) IsDirlike() bool

func (*FSONork) IsRoot

func (p *FSONork) IsRoot() bool

func (p *FSONork) IsDir() bool { return p.Nork.IsDir() }

func (*FSONork) RelFP

func (p *FSONork) RelFP() string

RelPath is a relative filepath. Discussion: it is the relative path of this Nork, relative to its tree's root Nork, which is the "local root" shared with other Norks in the same interconnected tree. (That is to say, a local root is the highest/topmost node of a directory tree imported in a single batch.) The last element of the relFP is this Nork's own name/label, analagous to FP.Base(Path).

type FSOTreeNorkFactory

type FSOTreeNorkFactory struct {
	// NexSeqID should be unique across all Norks,
	// so it should probably be filled in by SQLite.
	NexSeqID int
	// RootNork includes the [Filepaths].
	RootFSONork *FSONork
	// BatchID indexes a DB table.
	BatchID int
}

FSOTreeNorkFactory stores and tracks the state of an FSO (File System Object) Nork tree being assembled via calls to its method [NewFSOTreeNork].

It cannot (and should not) become invalid after creation, so Errer is not embedded & used.

A Factory is a good idea because then we can add arbitrary additional functionality (and complexity) and also spin up Nork factories that are customised for any of many purposes. .

func (*FSOTreeNorkFactory) NewFSOTreeNork

func (pFac *FSOTreeNorkFactory) NewFSOTreeNork(aRelPath string) *FSONork

NewFSOTreeNork prefers a relative path. It does not load file content, which is an expensive operation done elsewhere. But it loads the [Filepaths], which does checks specific to filesystem objects. Then the content can be loaded lazily at any time.

Do NOT use this for a path that is not a filesystem path. .

func (*FSOTreeNorkFactory) RootPath

func (p *FSOTreeNorkFactory) RootPath() string

type InspectorFunc

type InspectorFunc func(pNode *Nork) error

type Nork

type Nork struct {

	// Name is the only field that we know works across
	// all Use Cases, and any rules are specific to UC.
	Name string
	// Uuid7 is so that we can make & follow links btwn separate trees & lists.
	Uuid7 string
	// UC is Use Case is QuadraMode UC selector
	UC string

	// ==========
	//
	//	And also
	//
	// ==========
	// Errer is here because a Nork that is already linked into
	// a tree might acquire an error due to an external change.
	FU.Errer

	// -----------
	//  GUI Stuff
	// -----------
	HasFocus, IsSelected, IsExpanded, IsVisible bool
	// contains filtered or unexported fields
}

Nork is a "QuadraNode", handling four directions of connections for four distinct node user cases (UCs). Nork is kind of a maximalist implementation, and is subject to redefinition and getting slimmed down.

Paths need not be in the filesystem, and so paths are IAW package path rather than package path/filepath.

Child nodes ("Kid"s) have an externally-defined order, implying that child nodes are not directly [Comparable] amongst themselves. This specific specified order or child nodes is essential for representation of content.

(Child ordering can help in other contexts too, such as filesystem operations, but it turns out that the Go stdlib generally returns directory items in an order - lexical order - and walks directories in lexical order.)

The ordering lets us define funcs like FirstKid, NextPeer, PrevPeer, LastKid. They are defined in interface Norker.

Structs that embed Nork can use funcs to redefine the names and usage of fields in Nork. For example, a Nork embedded in a [Cnty] (Content Entity) used for a file-or-dir can provide access to non-public field `relPath` via `func RelFP()` (where FP is filepath).

Implementation of Interface

Methods on Nork need to be methods on pointers rather than methods on values. But the interface Norker will be defined on pointers rather than values, so the resulting syntax will be visually acceptable.

When a Nork is embedded in another struct, the embedding struct can rename the functions defined here. For example, [Nork.relPath] can be renamed as ´relFP`when we are dealing with a filesystem.

Directions in GUI:

  1. Left is ParentDir and Incoming Links
  2. Right is Kids and Outgoing Links
  3. Up is Incomiong Facets and maybe other metadata
  4. Down is Versions

Fields and use cases:

  • The field Prnt is for a "Parent" singleton of some type (whenever applicable).
  • The fields Tags and Vers are used pretty much the same in all UC's.
  • UC.FSO: File System Object: represent a dir-or-file-or-softlink: Here ordering is less important. Paths are valid FS paths. Prnt:dir, Kids:contents(!dir-v-file), Usrs:incoming-symlinks
  • UC.CMS: CMS Usage: Table of Contents line item (transclusion in CMS) This should be an ideal use case. Prnt:?TBD, Kids:sub-ToC's/outrefs, Usrs:inrefs+transcluders
  • UC.XML: XML text: tag/element in markup (XML, or other w AST) This has complexity handling same-named siblings, such as multiple <p> tags. Paths are tricky (mult. same tags). Prnt:parent-elm(or file @root), Kids:kid-elms, Usrs:?entity-stuf
  • UC.GST: GolangAST: Go code AST node (https://pkg.go.dev/go/ast#Node) Pnt:node(or root's file), Kids:AST, Usrs:callers/refs

Note that UUID might take on outsized importance, because it might be used to reference all manner of other DB tables and data structures and external (to the system) targets. It may become necessary to add prefixes to UUIDs to specify (e.g.) the DB tables of targets.

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 Norks! 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: In UC.1 we never have two same-named files in the same directory, but in UC.2 we might have (for example) multiple sibling <p> tags. So when representing markup, a map from paths to Norks would fail unless the tags are made unique with subscript indices (such as "[1]", "[2]").

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. Therefore a Nork 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 monify this code to define a simpler, more efficient variant of Nork that has unordered kids. .

func NewNork

func NewNork(aRelPath string) *Nork

NewNork does NOT assume an FS-type environment, and is fairly simple:

  • It expects a relative path, not absolute; "." and "./" are OK but not ""; an absolute path does NOT return an error
  • The path need not be in a filesystem, but paths are still expected to conform to package path (rather than package path/filepath): "The path package should only be used for paths separated by forward slashes, such as the paths in URLs. This package does not deal with Windows paths with drive letters or backslashes; to manipulate operating system paths, use the path/filepath package."
  • It does not attempt to resolve the path to an absolute path: this capability is not part of the `path` package
  • Its only concept is tree, not filesystem, so it mainly just records its own node name and acquires a UUID7
  • It implements interface [Errer], so errors can be acquired dynamically
  • There is no distinction between a "tree" Nork and a "lone" Nork

.

func (*Nork) AddKid

func (p *Nork) AddKid(aKid *Nork) *Nork

AddKid adds the supplied node as the last kid, and returns it (i.e. the new last kid), now linked into the tree. NOTE that just about ALL old (linked list) code is ripped out.

func (*Nork) AddKids

func (p *Nork) AddKids(rKids []*Nork) *Nork

AddKids adds the supplied nodes as kids, after any pre-existing kids, and returns the parent. The supplied nodes may NOT be already linked to each other as a linked list.

func (*Nork) CreatPath

func (p *Nork) CreatPath() string

func (*Nork) Debug

func (p *Nork) Debug() string

func (*Nork) Echo

func (p *Nork) Echo() string

func (*Nork) FirstKid

func (p *Nork) FirstKid() *Nork

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

func (*Nork) HasKids

func (p *Nork) HasKids() bool

HasKids is duh. func (p *Nork) [D any] HasKids() bool {

func (*Nork) Infos

func (p *Nork) Infos() string

func (*Nork) KidsAsSlice

func (p *Nork) KidsAsSlice() []*Nork

func (*Nork) LastKid

func (p *Nork) LastKid() *Nork

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

func (*Nork) Level

func (p *Nork) Level() int

func (*Nork) LinePrefixString

func (p *Nork) LinePrefixString() string

LinePrefixString provides indentation and should start a line of display/debug.

It does not end the string with (white)space. .

func (*Nork) NextPeer

func (p *Nork) NextPeer() *Nork

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

func (*Nork) Parent

func (p *Nork) Parent() *Nork

Parent returns the parent, duh. func (p *Nork) Parent() *Nork {

func (*Nork) PrevPeer

func (p *Nork) PrevPeer() *Nork

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

func (*Nork) PrintTree

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

func (*Nork) SetLevel

func (p *Nork) SetLevel(i int)

func (*Nork) SetNextPeer

func (p *Nork) SetNextPeer(p2 *Nork)

SetNextPeer has no side effects.

func (*Nork) SetParent

func (p *Nork) SetParent(p2 *Nork)

SetParent has no side effects.

func (*Nork) SetPrevPeer

func (p *Nork) SetPrevPeer(p2 *Nork)

SetPrevPeer has no side effects.

func (*Nork) StringserTree

func (p *Nork) StringserTree(f StringerFunc, w io.Writer) error

type Norker

type Norker interface {
	Stringser
	// =====================
	//  PATHS (Rel. & Abs.)
	// =====================
	// RelPath is rel.filepath for a file/dir, and for a DOM
	// node, it is meaningless, unless it is a [RootNorker],
	// for which it is the rel.path to the containing document.
	RelPath() string
	SetRelPath(string)
	// AbsPath 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 [RootNorker], for which it is
	// the abs.path to the containing document.
	AbsPath() string
	SetAbsPath(string)
	// ======================
	//  PATH CHARACTERISTICS
	// ======================
	// Level is zero-based (i.e. root Nork's is 0)
	Level() int
	// SetLevel(int) // should be self-calculated by node
	// Root should always return the root
	//  (at arena index 0, if one is used)
	Root() *Nork // RootNork
	IsRoot() bool
	IsDir() bool
	IsDirlike() bool
	// ==================
	//   NODE LINKS i.e.
	//  INTERCONNECTIONS
	// ==================
	Parent() *Nork
	SetParent(*Nork)
	HasKids() bool
	KidsAsSlice() []*Nork
	FirstKid() *Nork
	LastKid() *Nork
	PrevPeer() *Nork // cos "PrevKid" = iterator on the parent
	NextPeer() *Nork
	// AddKid returns the just-added kid, who
	// is added at the end of the list of Kids,
	// and who (TODO FWIW) knows his own arena
	// index (using [slices.Index])
	AddKid(*Nork) *Nork
	// AddKids returns the method target
	// - the parent of all the kids
	AddKids([]*Nork) *Nork
	ReplaceBy(*Nork) *Nork // returns Whom
	// ============
	//  MISCELLANY
	// ============
	LinePrefixString() string
	PrintTree(io.Writer) error
}

Norker is satisfied by *Nork NOT by Nork.

type StringFunc

type StringFunc func(Norker) string

StringFunc is used by interface Norker, so a method signature actually (MAYBE!) looks like:

func (*Nork) FuncName() string

type StringerFunc

type StringerFunc func(pNode *Nork) string

type Stringser

type Stringser interface {
	Echo() string
	Infos() string
	Debug() string
}

Stringser is copied from github.com/fbaube/stringutils.Stringser to keep out dependencies.

Jump to

Keyboard shortcuts

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