diff --git a/go.mod b/go.mod index 970afc8..f39d795 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,11 @@ module git.mstar.dev/mstar/treeificator go 1.24.2 -require git.mstar.dev/mstar/goutils v1.12.3 // indirect +require git.mstar.dev/mstar/goutils v1.12.3 + +require ( + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/rs/zerolog v1.33.0 // indirect + golang.org/x/sys v0.12.0 // indirect +) diff --git a/go.sum b/go.sum index bc5cf87..59add93 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,17 @@ git.mstar.dev/mstar/goutils v1.12.3 h1:Wx7i8/a99Cp+Y/XcXgqQr0r9cSsJu7QkWBlKyprTH44= git.mstar.dev/mstar/goutils v1.12.3/go.mod h1:juxY0eZEMnA95fedRp2LVXvUBgEjz66nE8SEdGKcxMA= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/informer.go b/informer.go index 66e3580..2b1ac00 100644 --- a/informer.go +++ b/informer.go @@ -8,6 +8,8 @@ type Informer interface { GetSuffix() string // Get the name of the node type. // Each name (in lowercase) may only exist once. The parser will enforce this. + // No guaranteed order for which informer will be used if multiple with the same name are in use. + // The name "default" is occupied by the built-in default informer for just strings GetName() string } diff --git a/parser.go b/parser.go index 05db78a..e882af6 100644 --- a/parser.go +++ b/parser.go @@ -1,13 +1,86 @@ package treeificator +import ( + "strings" + + "git.mstar.dev/mstar/goutils/other" +) + func Marshal(raw string, informers ...Informer) Node { prefixToInformer := map[string]Informer{} + maxPrefixLen := 0 + for _, informer := range informers { + prefixToInformer[informer.GetPrefix()] = informer + if l := len(informer.GetPrefix()); l > maxPrefixLen { + maxPrefixLen = l + } + } + defaultInformer := &DefaultInformer{} + delete(prefixToInformer, defaultInformer.GetPrefix()) + elems, consumed := marshal(raw, prefixToInformer, nil) + _ = consumed return Node{ - NodeType: &DefaultInformer{}, - Elements: marshal(raw, prefixToInformer), + NodeType: defaultInformer, + Elements: elems, } } -func marshal(raw string, informers map[string]Informer) []NodeElement { - return []NodeElement{NodeElement{Text: &raw}} +// Return the elements in the given string, using the given informers, in order of appearance. +func marshal( + raw string, + informers map[string]Informer, + activeInformer Informer, +) ([]NodeElement, uint64) { + elements := []NodeElement{} + buffer := make([]rune, len(raw)) + var consumed uint64 = 0 + var insideDiff uint64 = 0 // Nr of runes consumed by recursive calls + for i, char := range []rune(raw) { + // Skip runes that have been consumed by recursive calls + if uint64(i)+insideDiff < consumed { + continue + } + buffer = append(buffer, char) + consumed = uint64(i) + insideDiff + for k, v := range informers { + if !strings.HasSuffix(string(buffer), k) { + continue + } + if len(buffer) > 0 { + // Cut informer prefix from buffer, append buffer as text element + elements = append( + elements, + NodeElement{Text: other.IntoPointer(strings.TrimSuffix(string(buffer), k))}, + ) + } + buffer = make([]rune, uint64(len(raw))-consumed) + subElems, subConsumed := marshal(string([]rune(raw)[consumed:]), informers, v) + elements = append(elements, NodeElement{ + Node: &Node{ + NodeType: v, + Elements: subElems, + }, + }) + insideDiff += subConsumed + consumed += subConsumed + continue + } + if activeInformer != nil && strings.HasSuffix(string(buffer), activeInformer.GetSuffix()) { + if len(buffer) > 0 { + elements = append( + elements, + NodeElement{ + Text: other.IntoPointer( + strings.TrimSuffix(string(buffer), activeInformer.GetSuffix()), + ), + }, + ) + } + return elements, uint64(consumed) + } + } + if len(buffer) > 0 { + elements = append(elements, NodeElement{Text: other.IntoPointer(string(buffer))}) + } + return elements, uint64(consumed) }