diff --git a/README.md b/README.md index 38635d6..54ade6c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ This package depends upon the following third-party packages: - [`go-antlraci`](https://github.com/JesseCoretta/go-antlraci)\* - [`go-objectid`](https://github.com/JesseCoretta/go-objectid)\* - [`go-stackage`](https://github.com/JesseCoretta/go-stackage)\* + - [`go-shifty`](https://github.com/JesseCoretta/go-shifty)\* _\* Conceived and maintained by same author_ @@ -52,8 +53,7 @@ DIT because it considers you a threat. 🤓 ## About ACIs -Within the context of ACIv3, An [ACI](## "Access Control Instruction") is an expressive statement or "policy" that is used to define the disclosing or withholding of information -for an [X.500](## "ITU-T X-Series 500")/[LDAP](Lightweight Directory Access Protocol) [DIT](## "Directory Information Tree") as it pertains to its userbase. +Within the context of ACIv3, An [ACI](## "Access Control Instruction") is an expressive statement or "policy" that is used to define the disclosing or withholding of information for an [X.500](## "ITU-T X-Series 500")/[LDAP](## "Lightweight Directory Access Protocol") [DIT](## "Directory Information Tree") as it pertains to its userbase. In layperson's terms, ACIs are a specific and (largely) non-proprietary form of "LDAP permissions" that govern who can read, write, search, etc. @@ -108,8 +108,9 @@ to your vendor! - A package-wide cyclomatic complexity factor limit of nine (9) is imposed - We realize the standard recommended maximum is fifteen (15); we feel we can do better! - The following imported packages also exercise this philosophy: - - [`go-stackage`](http://github.com/JesseCoretta/go-stackage) - - [`go-objectid`](http://github.com/JesseCoretta/go-objectid) + - [`go-objectid`](https://github.com/JesseCoretta/go-objectid) + - [`go-stackage`](https://github.com/JesseCoretta/go-stackage) + - [`go-shifty`](https://github.com/JesseCoretta/go-shifty) - Compatible - Overall package design is meant to honor all of the facets of the ACIv3 specification **_in its entirety_** - No single vendor implementation is catered-to exclusively diff --git a/bind.go b/bind.go index 06d530a..53925f9 100644 --- a/bind.go +++ b/bind.go @@ -635,11 +635,14 @@ parsing request through go-antlraci. */ func convertBindRulesHierarchy(stack any) (BindContext, bool) { orig, _ := castAsStack(stack) - if orig.Len() == 0 { - return badBindRules, false - } + /* + if orig.Len() == 0 { + return badBindRules, false + } + */ var clean BindRules + var err error // Obtain the kind string from the // original stack. @@ -648,7 +651,7 @@ func convertBindRulesHierarchy(stack any) (BindContext, bool) { // Iterate the newly-populated clean // instance, performing type-casting // as needed, possibly in recursion. - for i := 0; i < orig.Len() && ok; i++ { + for i := 0; i < orig.Len() && ok && err == nil; i++ { slice, _ := orig.Index(i) // perform a type switch upon the @@ -682,11 +685,9 @@ func convertBindRulesHierarchy(stack any) (BindContext, bool) { // DistinguishedNames[] -> // [] -> // [] -> - if err := ntv.assertExpressionValue(); err == nil { + if err = ntv.assertExpressionValue(); err == nil { clean.Push(ntv) - continue } - break // slice is a stackage.Stack instance. // We want to cast to a BindRules type diff --git a/dn.go b/dn.go index 9c3c620..629b90d 100644 --- a/dn.go +++ b/dn.go @@ -789,6 +789,8 @@ keyword contexts: • `groupdn` • `roledn` + +Negated equality BindRule instances should be used with caution. */ func (r BindDistinguishedName) Ne() BindRule { x, ok := dnToCondition(r, Ne) @@ -809,6 +811,8 @@ keyword contexts: • `target_to` • `target_from` + +Negated equality TargetRule instances should be used with caution. */ func (r TargetDistinguishedName) Ne() TargetRule { x, ok := dnToCondition(r, Ne) @@ -1003,28 +1007,33 @@ func (r BindDistinguishedNames) setExpressionValues(key Keyword, values ...strin var U LDAPURI if U, err = parseLDAPURI(values[i], key.(BindKeyword)); err == nil { r.Push(U) - continue } - break - } - - // - // If the DN has the LocalScheme (ldap:///) - // prefix, we will chop it off as it is not - // needed in literal form any longer. - D := chopDNPfx(condenseWHSP(values[i])) - if len(D) < 3 || !(contains(D, `=`) || contains(D, `?`) || !isDNAlias(D)) { + } else { + // + // If the DN has the LocalScheme (ldap:///) + // prefix, we will chop it off as it is not + // needed in literal form any longer. + D := chopDNPfx(condenseWHSP(values[i])) err = illegalSyntaxPerTypeErr(D, r.Keyword()) - return + if !isInvalidDNSyntax(D) && !contains(D, `?`) { + err = nil + // Push DN into receiver + r.Push(BindDistinguishedName{newDistinguishedName(D, key)}) + } } - // Push DN into receiver - r.Push(BindDistinguishedName{newDistinguishedName(D, key)}) + if err != nil { + break + } } return } +func isInvalidDNSyntax(dn string) bool { + return (len(dn) < 3 || !(contains(dn, `=`) || !isDNAlias(dn))) +} + /* setExpressionValues is a private method called by assertTargetTFDN for DN-based Target Rules parsing. @@ -1041,7 +1050,7 @@ func (r TargetDistinguishedNames) setExpressionValues(key Keyword, values ...str // prefix, we will chop it off as it is not // needed in literal form any longer. D := chopDNPfx(condenseWHSP(values[i])) - if len(D) < 3 || !(contains(D, `=`) || !isDNAlias(D)) { + if isInvalidDNSyntax(D) { err = illegalSyntaxPerTypeErr(D, r.Keyword()) return } diff --git a/filter.go b/filter.go index 327e458..4783237 100644 --- a/filter.go +++ b/filter.go @@ -1122,12 +1122,8 @@ func hasAttributeFilterOperationPrefix(raw string) bool { return false } -/* -parseAttributeFilterOperations processes the raw input value into an instance of -AttributeFilterOperations, which is returned alongside an error instance. -*/ -func parseAttributeFilterOperations(raw string, delim int) (afos AttributeFilterOperations, err error) { - var char rune = rune(44) // ASCII #44 [comma, default] +func afosDelim(delim int) (char rune) { + char = rune(44) // ASCII #44 [comma, default] // If delim is anything except one (1) // use the default, else use semicolon. @@ -1135,6 +1131,16 @@ func parseAttributeFilterOperations(raw string, delim int) (afos AttributeFilter char = rune(59) // ASCII #59 [semicolon] } + return +} + +/* +parseAttributeFilterOperations processes the raw input value into an instance of +AttributeFilterOperations, which is returned alongside an error instance. +*/ +func parseAttributeFilterOperations(raw string, delim int) (afos AttributeFilterOperations, err error) { + char := afosDelim(delim) + // Scan the raw input value and count the number of // occurrences of an AttributeOperation prefix. var opct int @@ -1174,7 +1180,7 @@ func parseAttributeFilterOperations(raw string, delim int) (afos AttributeFilter // is an AttributeFilterOperation instance // // e.g.: add=objectClass:(&(employeeStatus:active)(c=US)) - for i := 0; i < len(vals); i++ { + for i := 0; i < len(vals) && err == nil; i++ { var afo AttributeFilterOperation value := unquote(condenseWHSP(vals[i])) @@ -1193,9 +1199,7 @@ func parseAttributeFilterOperations(raw string, delim int) (afos AttributeFilter // instance into our AttributeFilterOperations // stack instance. afos.Push(afo) - continue } - break } return @@ -1224,13 +1228,11 @@ func parseAttributeFilterOperation(raw string) (afo AttributeFilterOperation, er afo.setCategory(cat) seq = split(trimS(val), `&&`) - for j := 0; j < len(seq); j++ { + for j := 0; j < len(seq) && err == nil; j++ { var af AttributeFilter if af, err = parseAttributeFilter(trimS(seq[j])); err == nil { afo.Push(af) - continue } - break } } diff --git a/go.mod b/go.mod index 986355b..01cd909 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( ) require ( + github.com/JesseCoretta/go-shifty v1.0.0-stable.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect ) diff --git a/go.sum b/go.sum index 17f07e0..f02b159 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ github.com/JesseCoretta/go-objectid v0.0.6-alpha.0 h1:2WjaaZMnC5BEKckaBy0txCDSH7 github.com/JesseCoretta/go-objectid v0.0.6-alpha.0/go.mod h1:dOPQhGxLieMBl4WF1gq0Z3yb3KppfNGV/XrPrudjyuw= github.com/JesseCoretta/go-objectid v0.0.6-alpha.1 h1:aq+kKX3rZaKUl8XSAOFqzMTw6YZcrX2cSImbysw6BVE= github.com/JesseCoretta/go-objectid v0.0.6-alpha.1/go.mod h1:dOPQhGxLieMBl4WF1gq0Z3yb3KppfNGV/XrPrudjyuw= +github.com/JesseCoretta/go-shifty v1.0.0-stable.0 h1:ELY5r6moVZeR8PrbZvZco52mzzFmHbv4xKUUmKs4nV8= +github.com/JesseCoretta/go-shifty v1.0.0-stable.0/go.mod h1:vnqi9wCMnLDDD4XU3NmL2fF7dz4HiaAuI/M3bqB2bQE= github.com/JesseCoretta/go-stackage v0.0.1-alpha.1 h1:MzrDf4Sp7q/SghQf6Li3QQY/4kPjAIlyQtMOSKInpTU= github.com/JesseCoretta/go-stackage v0.0.1-alpha.1/go.mod h1:kfcMUggHyU8LQOldjir3KdXkZMeh1E/FWivmuPklZyQ= github.com/JesseCoretta/go-stackage v0.0.1-alpha.2 h1:kLGzZ93eUUZDwbJ4bTz5ujAZUAPGnltPPUN4SlOIwsw= diff --git a/levels.go b/levels.go index c6c63ed..93f0ed7 100644 --- a/levels.go +++ b/levels.go @@ -7,7 +7,7 @@ within the ACIv3 standard. // Maps for resolving level instances var ( - levelBitIter = bitSize(Levels(0)) - 4 // we don't use all of uint16, no sense iterating the whole thing + levelBitIter = bitSize(uint16(0)) - 4 // we don't use all of uint16, no sense iterating the whole thing levelMap = make(map[int]Level, 0) levelNumbers = make(map[string]Level, 0) ) @@ -46,7 +46,7 @@ instances of Inheritance. It contains a Level bit container and an AttributeBindTypeOrValue instance. */ type inheritance struct { - Levels + *levels AttributeBindTypeOrValue } @@ -75,23 +75,13 @@ func newInheritance(x AttributeBindTypeOrValue, lvl ...any) (i *inheritance) { /* Level describes a discrete numerical abstract of a subordinate level. Level describes any single Level definition. Level constants are intended for "storage" -within an instance of Levels -- the compound counterpart of this type. +within an instance of Inheritance. Valid Level constants are level zero (0) through level nine (9), though this will vary across implementations. */ type Level uint16 -/* -Levels is the compound (bitshifted) counterpart to the scalar Level type. - -Levels can express all combinations Level values; for example, if a Levels -instance has an underlying value of three (3), this describes the presence -of the individual Level0 (1) and Level1 (2) definitions within said value -(i.e.: 1+2=3). -*/ -type Levels uint16 - /* IsZero returns a Boolean value indicative of whether the receiver instance is nil, or unset. @@ -155,23 +145,25 @@ Eq initializes and returns a new BindRule instance configured to express the evaluation of the receiver value as Equal-To the `userattr` or `groupattr` Bind keyword context. */ -func (r Inheritance) Eq() BindRule { - if err := r.Valid(); err != nil { - return badBindRule +func (r Inheritance) Eq() (b BindRule) { + if err := r.Valid(); err == nil { + b = BR(r.inheritance.AttributeBindTypeOrValue.BindKeyword, Eq, r) } - return BR(r.inheritance.AttributeBindTypeOrValue.BindKeyword, Eq, r) + return } /* Ne initializes and returns a new BindRule instance configured to express the evaluation of the receiver value as Not-Equal-To the `userattr` or `groupattr` Bind keyword context. + +Negated equality BindRule instances should be used with caution. */ -func (r Inheritance) Ne() BindRule { - if err := r.Valid(); err != nil { - return badBindRule +func (r Inheritance) Ne() (b BindRule) { + if err := r.Valid(); err == nil { + b = BR(r.inheritance.AttributeBindTypeOrValue.BindKeyword, Ne, r) } - return BR(r.inheritance.AttributeBindTypeOrValue.BindKeyword, Ne, r) + return } /* @@ -210,7 +202,6 @@ func parseInheritance(inh string) (I Inheritance, err error) { // the identifier sequence. idx := idxr(raw, ']') if idx == -1 { - // non conformant! err = badInhErr(inh) return } @@ -226,17 +217,18 @@ func parseInheritance(inh string) (I Inheritance, err error) { // Initialize our return instance, as we're about // to begin storing things in it. I = Inheritance{new(inheritance)} + I.levels = newLvls() // Iterate the split sequence of level identifiers. // Also, obliterate any ASCII #32 (SPACE) chars // (e.g.: ', ' -> ','). for _, r := range split(repAll(raw[:idx], ` `, ``), `,`) { - I.inheritance.shift(r) // left shift + I.inheritance.shift(r) } // Bail if nothing was found (do not fall // back to default when parsing). - if I.inheritance.Levels == Levels(noLvl) { + if I.inheritance.levels.cast().Int() == 0 { // bogus or unsupported identifiers? err = levelsNotFoundErr() return @@ -249,11 +241,10 @@ func parseInheritance(inh string) (I Inheritance, err error) { // boundary (see above). var abv AttributeBindTypeOrValue - if abv, err = parseATBTV(raw[idx+2:]); err != nil { - // non conformant ATBTV - return + if abv, err = parseATBTV(raw[idx+2:]); err == nil { + I.inheritance.AttributeBindTypeOrValue = abv } - I.inheritance.AttributeBindTypeOrValue = abv + return } @@ -265,7 +256,7 @@ would represent an abstract length of two (2). */ func (r Inheritance) Len() int { var D int - for i := 0; i < bitSize(noLvl); i++ { + for i := 0; i < levelBitIter; i++ { if d := Level(1 << i); r.Positive(d) { D++ } @@ -274,16 +265,6 @@ func (r Inheritance) Len() int { return D } -/* -Levels returns the compound Levels instance within the receiver. -*/ -func (r Inheritance) Levels() Levels { - if r.IsZero() { - return Levels(noLvl) - } - return r.inheritance.Levels -} - /* Keyword returns the Keyword associated with the receiver instance. In the context of this type instance, the Keyword returned will be either @@ -311,32 +292,14 @@ The return value(s) are enclosed within square-brackets, followed by comma delimitation and are prefixed with "parent" before being returned. */ -func (r Inheritance) String() string { - if err := r.Valid(); err != nil { - return badInheritance +func (r Inheritance) String() (s string) { + s = badInheritance + if err := r.Valid(); err == nil { + // string representation of Levels sequence + lvls := r.inheritance.levels.string() + s = sprintf("parent[%s].%s", lvls, + r.inheritance.AttributeBindTypeOrValue) } - - // string representation of Levels sequence - lvls := r.inheritance.Levels.String() - - return sprintf("parent[%s].%s", lvls, - r.inheritance.AttributeBindTypeOrValue) -} - -/* -Len returns the abstract integer length of the receiver, quantifying -the number of Level instances currently being expressed. For example, -if the receiver instance has its Level4 and Level7 bits enabled, this -would represent an abstract length of two (2). -*/ -func (r Levels) Len() (l int) { - for i := 0; i < levelBitIter; i++ { - shift := Level(1 << i) - if r.Positive(shift) { - l++ - } - } - return } @@ -344,15 +307,12 @@ func (r Levels) Len() (l int) { String is a string method that returns the string representation of the receiver instance. */ -func (r Levels) String() string { - var levels []string - if r == Levels(noLvl) { - // No levels? default to level 0 (baseObject) - levels = append(levels, Level0.String()) - } else { +func (r levels) string() string { + var levels []string = []string{Level0.String()} + if r.cast().Int() > 0 { + levels = make([]string, 0) for i := 0; i < levelBitIter; i++ { - shift := Level(1 << i) - if r.Positive(shift) { + if shift := Level(1 << i); r.cast().Positive(shift) { levels = append(levels, shift.String()) } } @@ -384,51 +344,36 @@ func (r Level) Compare(x any) bool { } /* -Shift wraps Levels.Shift via the underlying Levels value -found within the receiver instance. +Shift wraps go-shifty's BitValue.Shift method. */ func (r Inheritance) Shift(x ...any) Inheritance { if r.IsZero() { r = Inheritance{new(inheritance)} } - r.inheritance.Levels.shift(x...) - return r -} - -/* -Shift shifts the receiver instance of Levels to include Level -x, if not already present. -*/ -func (r *Levels) Shift(x ...any) *Levels { - r.shift(x...) + r.inheritance.shift(x...) return r } /* shift is a private method called by the Shift method. */ -func (r *Levels) shift(x ...any) { +func (r *inheritance) shift(x ...any) { + if r.levels == nil { + r.levels = newLvls() + } + for i := 0; i < len(x); i++ { var lvl Level switch tv := x[i].(type) { case Level: - if tv != noLvl { - lvl = tv - } + lvl = tv case int: - if lvl = assertIntInheritance(tv); lvl == noLvl { - continue - } + lvl = assertIntInheritance(tv) case string: - if lvl = assertStrInheritance(tv); lvl == noLvl { - continue - } - default: - continue + lvl = assertStrInheritance(tv) } - - (*r) |= Levels(lvl) + (*r.levels).cast().Shift(lvl) } } @@ -439,7 +384,6 @@ user. Valid levels are zero (0) through four (4), else noLvl is returned. */ func assertStrInheritance(x string) (lvl Level) { - lvl = noLvl for k, v := range levelNumbers { if x == k { lvl = v @@ -457,138 +401,75 @@ user. Valid levels are zero (0) through four (4), else noLvl is returned. */ func assertIntInheritance(x int) (lvl Level) { - lvl = noLvl if L, found := levelMap[x]; found { lvl = L - return } return } /* -Positive returns a Boolean value indicative of whether -the receiver instance of Levels includes Level x. +Positive wraps go-shifty's BitValue.Positive method. */ -func (r Inheritance) Positive(x any) bool { - if r.IsZero() { - r = Inheritance{new(inheritance)} +func (r Inheritance) Positive(x any) (posi bool) { + if !r.IsZero() { + posi = r.inheritance.positive(x) } - return r.inheritance.Levels.positive(x) -} - -/* -Positive returns a Boolean value indicative of whether -the receiver has the appropriate bits enabled to include -abstract value x, which describes a Level definition. -*/ -func (r Levels) Positive(x any) bool { - return r.positive(x) + return } /* IsZero returns a Boolean value indicative of whether the receiver is in an aberrant state. */ -func (r Levels) IsZero() bool { - return int(r) == 0 -} - -/* -Compare returns a Boolean value indicative of a SHA-1 comparison -between the receiver (r) and input value x. -*/ -func (r Levels) Compare(x any) bool { - return compareHashInstance(r, x) -} - -/* -Valid returns an error instance that describes the undesirable -state of the receiver, if applicable. A nil error is returned -otherwise. -*/ -func (r Levels) Valid() (err error) { - if r.IsZero() { - err = nilInstanceErr(r) - } - - // TODO: additional checks? - return -} +//func (r levels) IsZero() bool { +// return r.cast().Int() == 0 +//} /* positive is a private method executed by the Positive method. */ -func (r Levels) positive(x any) (posi bool) { - if r.IsZero() { - return - } - +func (r inheritance) positive(x any) (posi bool) { var lvl Level switch tv := x.(type) { case Level: - if tv != noLvl { - lvl = tv - } + lvl = tv case int: lvl = assertIntInheritance(tv) case string: lvl = assertStrInheritance(tv) } + posi = (*r.levels).cast().Positive(lvl) - posi = (r & Levels(lvl)) > 0 return } /* -Unshift wraps Levels.Unshift via the underlying Levels value -found within the receiver instance. +Unshift wraps go-shifty's BitValue.Unshift method. */ func (r Inheritance) Unshift(x ...any) Inheritance { - if r.IsZero() { - r = Inheritance{new(inheritance)} + if !r.IsZero() { + r.inheritance.unshift(x...) } - - r.inheritance.Levels.unshift(x...) - return r -} - -/* -Unshift right-shifts the receiver instance of Levels to -remove Level x, if present. -*/ -func (r *Levels) Unshift(x ...any) *Levels { - r.unshift(x...) return r } /* unshift is a private method called by the Unshift method. */ -func (r *Levels) unshift(x ...any) { +func (r *inheritance) unshift(x ...any) { for i := 0; i < len(x); i++ { var lvl Level - switch tv := x[0].(type) { + switch tv := x[i].(type) { case Level: - if tv != noLvl { - lvl = tv - } + lvl = tv case int: - if lvl = assertIntInheritance(tv); lvl == noLvl { - continue - } + lvl = assertIntInheritance(tv) case string: - if lvl = assertStrInheritance(tv); lvl == noLvl { - continue - } - default: - continue + lvl = assertStrInheritance(tv) } - - (*r) = (*r) &^ Levels(lvl) + (*r.levels).cast().Unshift(lvl) } - - return } const badInheritance = `` diff --git a/levels_test.go b/levels_test.go index 5f68b97..6f72772 100644 --- a/levels_test.go +++ b/levels_test.go @@ -73,14 +73,6 @@ func ExampleInheritance_Positive() { // Output: Level 5 positive? false } -func ExampleInheritance_Levels() { - attr := AT(`manager`) - uat := UAT(attr, AV(`uid=frank,ou=People,dc=example,dc=com`)) - inh := Inherit(uat, 1, 3) - fmt.Printf("Levels: %s", inh.Levels()) - // Output: Levels: 1,3 -} - func ExampleInheritance_Shift() { attr := AT(`manager`) uat := UAT(attr, AV(`uid=frank,ou=People,dc=example,dc=com`)) @@ -95,10 +87,12 @@ func ExampleInheritance_Unshift() { attr := AT(`manager`) uat := UAT(attr, AV(`uid=frank,ou=People,dc=example,dc=com`)) inh := Inherit(uat, 1, 3, 8) - inh.Unshift(1) // we changed our mind + inh.Unshift(1) // we changed our mind + inh.Unshift(`1`) // alternatively + inh.Unshift(Level1) // alternatively - fmt.Printf("Number of levels: %d [%s]", inh.Len(), inh.Levels()) - // Output: Number of levels: 2 [3,8] + fmt.Printf("Number of levels: %d", inh.Len()) + // Output: Number of levels: 2 } func ExampleInheritance_Keyword() { @@ -120,62 +114,17 @@ func ExampleInheritance_Keyword() { // Output: Keyword: userattr } -func ExampleLevels_IsZero() { - var l Levels - fmt.Printf("Zero: %t", l.IsZero()) - // Output: Zero: true -} - -func ExampleLevels_Valid() { - var l Levels - fmt.Printf("Valid: %t", l.Valid() == nil) - // Output: Valid: false -} - -func ExampleLevels_Shift() { - var l Levels - l.Shift(Level4, Level0) // variadic style - l.Shift(Level1). // fluent ... - Shift(Level6) // ... style - l.Shift(2) // lazy ints supported too! - fmt.Printf("%d Levels: %s", l.Len(), l) - // Output: 5 Levels: 0,1,2,4,6 -} - -func ExampleLevels_Len() { - var l Levels - l.Shift(Level4, Level0) - fmt.Printf("Level count: %d", l.Len()) - // Output: Level count: 2 -} - -func ExampleLevels_Positive() { - var l Levels - l.Shift(Level4, Level0) - fmt.Printf("Level 4 positive? %t", l.Positive(4)) - // Output: Level 4 positive? true -} - -func ExampleLevels_Positive_byString() { - var l Levels - l.Shift(Level4, Level0) - fmt.Printf("Level 4 positive? %t", l.Positive(`4`)) - // Output: Level 4 positive? true -} - -func ExampleLevels_Unshift() { - var l Levels - l.Shift(Level4, Level0) - l.Unshift(Level0) - fmt.Printf("Levels: %s", l) - // Output: Levels: 4 -} - -func ExampleLevels_String() { - var l Levels - l.Shift(0, 1, 2, 3, 4, 5, 6, 7, 8, 9) - fmt.Printf("%d Levels: %s", l.Len(), l) - // Output: 10 Levels: 0,1,2,3,4,5,6,7,8,9 +func ExampleInheritance_Positive_byString() { + attr := AT(`manager`) + // we'll use the userattr keyword (bestowed + // by UAT), and for a value we'll just give + // it an explicit bind type (USERDN). If it + // is preferable to use groupattr keyword, + // simply supplant UAT with GAT func. + uat := UAT(attr, USERDN) + inh := Inherit(uat, 6, 7) // levels 6 & 7 + fmt.Printf("Level 6 positive? %t", inh.Positive(`6`)) + // Output: Level 6 positive? true } func TestInheritance(t *testing.T) { @@ -239,27 +188,7 @@ func ExampleLevel_Compare() { // Output: Hashes are equal: false } -/* -This example demonstrates the SHA-1 hash comparison between two (2) -Level instances using the Compare method. -*/ -func ExampleLevels_Compare() { - var l1, l2 Levels - l1.Shift(Level1, 3) - l2.Shift(Level2, 3, 4) - fmt.Printf("Hashes are equal: %t", l1.Compare(l2)) - // Output: Hashes are equal: false -} - func TestLevels_bogus(t *testing.T) { - var l1 Levels - _ = l1.String() - _ = l1.Positive(noLvl) - _ = l1.Positive(Level8) - _ = l1.positive(8) - _ = l1.positive(`1`) - _ = l1.positive(`this`) - var inh Inheritance if err := inh.Valid(); err == nil { t.Errorf("%s failed: invalid %T returned no validity error", @@ -291,7 +220,8 @@ func TestLevels_bogus(t *testing.T) { return } - for _, rawng := range []string{ + for idx, rawng := range []string{ + `100.manager#USERDN`, `parent[100].manager#USERDN`, `parent[].manager#SELFDN`, `parent[4]#ROLEDN`, @@ -304,15 +234,15 @@ func TestLevels_bogus(t *testing.T) { } { i, err := parseInheritance(rawng) if err == nil { - t.Errorf("%s failed: parsing of bogus %T definition returned no error", - t.Name(), i) + t.Errorf("%s failed [idx:%d]: parsing of bogus %T definition returned no error (%s)", + t.Name(), idx, i, rawng) return } if i.String() != badInheritance { - t.Errorf("%s failed: %T parsing attempt failed; want '%s', got '%s'", - t.Name(), i, badInheritance, i) + t.Errorf("%s failed [idx:%d]: %T parsing attempt failed; want '%s', got '%s'", + t.Name(), idx, i, badInheritance, i) return } } @@ -361,7 +291,7 @@ func TestInheritance_parse(t *testing.T) { func TestInheritance_codecov(t *testing.T) { var inh Inheritance - _ = inh.Levels().Positive(`4`) + _ = inh.Positive(`4`) _ = inh.Keyword() _ = inh.String() _ = inh.Shift(1370) @@ -375,61 +305,8 @@ func TestInheritance_codecov(t *testing.T) { _ = inh.Positive(`fart`) _ = inh.Positive(100000) _ = inh.Positive(-1) - _ = inh.Levels().Positive(4) - _ = inh.Levels().Positive("something awful") + _ = inh.Positive(4) + _ = inh.Positive("something awful") _ = inh.Positive(Level(^uint16(0))) _ = inh.Positive(3.14159) } - -/* -func TestInheritance_lrShift(t *testing.T) { - var p Inheritance - - // three iterations, one per supported - // Level type - for i := 0; i < 3; i++ { - - // iterate each of the levels in the - // levels/names map - for k, v := range levelMap { - - term, typ := testGetLevelsTermType(i, k, v.String()) - - shifters := map[int]func(...any) Inheritance{ - 0: p.Shift, - 1: p.Unshift, - } - - for j := 0; j < len(shifters); j++ { - mode, phase := testGetLevelsPhase(j) - if shifters[j](term); p.Positive(term) != phase { - t.Errorf("%s failed: %T %s %s failed [key:%d; term:%v] (value:%v)", - t.Name(), p, typ, mode, k, term, p) - } - } - } - } -} - -func testGetLevelsPhase(j int) (mode string, phase bool) { - mode = `shift` - if phase = (0 == j); !phase { - mode = `un` + mode - } - - return -} - -func testGetLevelsTermType(i,k int, v string) (term any, typ string) { - term = k // default - switch i { - case 1: - term = v // string name (e.g.: `0`) - case 2: - term = Level(k) // Level - } - typ = sprintf("%T", term) // label for err - - return -} -*/ diff --git a/net.go b/net.go index 54aaf8b..3dcc135 100644 --- a/net.go +++ b/net.go @@ -67,6 +67,8 @@ func (r IPAddr) Eq() BindRule { /* Ne initializes and returns a new BindRule instance configured to express the evaluation of the receiver value as Not-Equal-To the `ip` Bind keyword context. + +Negated equality BindRule instances should be used with caution. */ func (r IPAddr) Ne() BindRule { if err := r.Valid(); err != nil { @@ -459,6 +461,8 @@ func (r FQDN) Eq() BindRule { /* Ne initializes and returns a new BindRule instance configured to express the evaluation of the receiver value as Not-Equal-To the `dns` Bind keyword context. + +Negated equality BindRule instances should be used with caution. */ func (r FQDN) Ne() BindRule { if err := r.Valid(); err != nil { diff --git a/parse.go b/parse.go index 7e234dc..6e8bc8d 100644 --- a/parse.go +++ b/parse.go @@ -508,7 +508,7 @@ func processTargetRules(stack any) (TargetRules, error) { // identifying each by integer index i. Try to // marshal the parser.RuleExpression contents // into the appropriate go-aci type. - for i := 0; i < t.Len(); i++ { + for i := 0; i < t.Len() && err == nil; i++ { trv := t.Index(i) // Extract individual expression value @@ -526,10 +526,7 @@ func processTargetRules(stack any) (TargetRules, error) { // DistinguishedNames[] -> // [] -> // [] -> - if err = trv.assertExpressionValue(); err == nil { - continue // because codecov. - } - break + err = trv.assertExpressionValue() } return t, err @@ -839,8 +836,8 @@ func parsePermission(raw string) (*permission, error) { func unpackageAntlrPermission(perm parser.Permission) (*permission, error) { p := &permission{ - bool: new(bool), // disposition (ptr to bool) - Right: new(Right), // rights specifiers (ptr to uint8 alias) + bool: new(bool), // disposition (ptr to bool) + rights: newRights(), // rights specifiers (embedded shifty.BitValue) } // process each permission one at a time @@ -862,8 +859,9 @@ func unpackageAntlrPermission(perm parser.Permission) (*permission, error) { // rather through bit summation of the underlying // values defined in go-aci as part of its attempt // to be memory efficient. - err := unexpectedValueCountErr(`permission bits`, bits, int(*p.Right)) - if bits == int(*p.Right) { + rint := p.rights.cast().Int() + err := unexpectedValueCountErr(`permission bits`, bits, rint) + if bits == rint { // !! WARNING - EXTREME SECURITY RISK !! // // A disposition has two (2) official settings and, thus, @@ -945,15 +943,13 @@ func processPermissionBindRules(stack any) (pbrs PermissionBindRules, err error) _pbrs, _ := castAsStack(stack) pbrs = PBRs() - for i := 0; i < _pbrs.Len(); i++ { + for i := 0; i < _pbrs.Len() && err == nil; i++ { slice, _ := _pbrs.Index(i) if _pbr, asserted := slice.(parser.PermissionBindRule); asserted { var pbr PermissionBindRule if pbr, err = processPermissionBindRule(_pbr); err == nil { pbrs.Push(pbr) - continue } - break } } diff --git a/rights.go b/rights.go index f632d04..bf598d8 100644 --- a/rights.go +++ b/rights.go @@ -45,7 +45,7 @@ type Permission struct { type permission struct { *bool - *Right + *rights } /* @@ -71,7 +71,7 @@ bearing the provided disposition and Right instance(s). func newPermission(disp bool, x ...any) (p *permission) { p = new(permission) p.bool = &disp - p.Right = new(Right) + p.rights = newRights() p.shift(x...) return p } @@ -83,12 +83,12 @@ func (r *permission) shift(x ...any) { // of a Right). for i := 0; i < len(x); i++ { switch tv := x[i].(type) { - case int: - r.shiftIntRight(tv) + case int, Right: + r.rights.cast().Shift(tv) case string: - r.shiftStrRight(tv) - case Right: - r.shiftIntRight(int(tv)) + if priv, found := rightsNames[lc(tv)]; found { + r.rights.cast().Shift(priv) + } } } } @@ -101,152 +101,38 @@ func (r *permission) unshift(x ...any) { // of a Right). for i := 0; i < len(x); i++ { switch tv := x[i].(type) { - case int: - r.unshiftIntRight(tv) + case int, Right: + r.rights.cast().Unshift(tv) case string: - r.unshiftStrRight(tv) - case Right: - r.unshiftIntRight(int(tv)) + if priv, found := rightsNames[lc(tv)]; found { + r.rights.cast().Unshift(priv) + } } } } } -func (r *permission) shiftIntRight(i int) { - // avoid over/under flow - if !(0 <= i && i <= int(^uint16(0))) { - return - } - - if _, matched := noneOrFullAccessRights(Right(i)); matched { - (*r.Right) = Right(i) - return - } - - if rightsPowerOfTwo(i) { - (*r.Right) |= Right(i) - } -} - -func (r *permission) unshiftIntRight(i int) { - // avoid over/under flow - if !(0 <= i && i <= int(^uint16(0))) { - return - } - - if R, matched := noneOrFullAccessRights(Right(i)); matched { - (*r.Right) = (*r.Right) &^ R - return - } - - if rightsPowerOfTwo(i) { - (*r.Right) = (*r.Right) &^ Right(i) - } -} - -func (r *permission) shiftStrRight(s string) { - if priv, found := rightsNames[lc(s)]; found { - (*r.Right) |= priv - } -} - -func (r *permission) unshiftStrRight(s string) { - if priv, found := rightsNames[lc(s)]; found { - (*r.Right) = (*r.Right) &^ priv - } -} - -func rightsPowerOfTwo(x int) bool { - return isPowerOfTwo(x) && (0 <= x && x <= int(^uint16(0))) -} - -func (r permission) positive(x any) (posi bool) { +func (r *permission) positive(x any) (posi bool) { if !r.isZero() { switch tv := x.(type) { case int: - posi = r.positiveIntRight(tv) + if posi = tv == 0 && r.rights.cast().Int() == tv; posi { + break + } + posi = r.rights.cast().Positive(tv) case string: - posi = r.positiveStrRight(tv) + if priv, found := rightsNames[lc(tv)]; found { + posi = r.positive(priv) + } case Right: - posi = r.positiveIntRight(int(tv)) + posi = r.positive(int(tv)) } } return } -func (r permission) positiveIntRight(i int) (posi bool) { - // avoid over/under flow - if !(0 <= i && i <= int(^uint16(0))) { - return - } - - if _, matched := noneOrFullAccessRights(Right(i)); matched { - if i == 0 { - posi = int(*r.Right) == i - } else { - posi = ((*r.Right) & AllAccess) > 0 - } - return - } - - if rightsPowerOfTwo(i) { - posi = ((*r.Right) & Right(i)) > 0 - } - - return -} - -func (r permission) positiveStrRight(s string) (posi bool) { - if R, matched := noneOrFullAccessString(lc(s), (*r.Right)); matched { - if R == NoAccess { - posi = int(*r.Right) == 0 - } else { - posi = ((*r.Right) & AllAccess) > 0 - } - return - } - - // Resolve the name of a Right into a Right. - if priv, found := rightsNames[lc(s)]; found { - posi = ((*r.Right) & priv) > 0 - } - - return -} - -func noneOrFullAccessRights(x Right) (Right, bool) { - var matched bool - if x == NoAccess { - // NoAccess should stop the party - // dead in its tracks, regardless - // of any other iterated value. - matched = true - - } else if x == AllAccess { - // AllAccess should stop the party - // dead in its tracks, regardless - // of any other iterated value. - matched = true - } - - return x, matched -} - -func noneOrFullAccessString(x string, r Right) (R Right, matched bool) { - switch { - case x == NoAccess.String(): - R = NoAccess - matched = int(r) == 0 // 0 - case x == AllAccess.String(): - R = AllAccess - matched = int(r) == 895 // *895 - } - - return -} - /* String is a stringer method that returns a single string name value for receiver instance of Right. */ @@ -278,18 +164,17 @@ the number of Right instances currently being expressed. For example, if the receiver instance has its Read and Delete Right bits enabled, this would represent an abstract length of two (2). */ -func (r Permission) Len() int { - if r.IsZero() { - return 0 +func (r Permission) Len() (l int) { + if !r.IsZero() { + l = r.permission.len() } - - return r.permission.len() + return } func (r permission) len() int { var D int - for i := 0; i < bitSize(NoAccess); i++ { - if d := Right(1 << i); r.positive(d) { + for i := 0; i < r.rights.cast().Size(); i++ { + if d := Right(1 << i); r.rights.cast().Positive(d) { D++ } } @@ -305,23 +190,24 @@ func (r Permission) String() string { if r.IsZero() { return badPerm } + pint := r.permission.rights.cast().Int() var rights []string - if (*r.permission.Right) == AllAccess { + if Right(pint) == AllAccess { rights = append(rights, AllAccess.String()) return r.sprintf(rights) - } else if (*r.permission.Right) == Right(1023) { + } else if pint == 1023 { rights = append(rights, AllAccess.String()) rights = append(rights, ProxyAccess.String()) return r.sprintf(rights) - } else if (*r.permission.Right) == NoAccess { + } else if Right(pint) == NoAccess { rights = append(rights, NoAccess.String()) return r.sprintf(rights) } - for i := 0; i < bitSize(NoAccess); i++ { - right := Right(1 << i) - if r.Positive(right) { + size := r.permission.rights.cast().Size() + for i := 0; i < size; i++ { + if right := Right(1 << i); r.Positive(right) { rights = append(rights, right.String()) } } @@ -364,23 +250,21 @@ func (r permission) disposition() (disp string) { /* Positive returns a Boolean value indicative of whether a particular bit is positive (is set). Negation implies negative, or unset. */ -func (r Permission) Positive(x any) bool { - if err := r.Valid(); err != nil { - return false +func (r Permission) Positive(x any) (posi bool) { + if err := r.Valid(); err == nil { + posi = r.permission.positive(x) } - return r.permission.positive(x) + return } /* Shift left-shifts the receiver instance to include Right x, if not already present. */ func (r Permission) Shift(x ...any) Permission { - if err := r.Valid(); err != nil { - return r - } - - for i := 0; i < len(x); i++ { - r.permission.shift(x[i]) + if err := r.Valid(); err == nil { + for i := 0; i < len(x); i++ { + r.permission.shift(x[i]) //rights.cast().Shift(x[i]) + } } return r } @@ -389,12 +273,10 @@ func (r Permission) Shift(x ...any) Permission { Unshift right-shifts the receiver instance to remove Right x, if present. */ func (r Permission) Unshift(x ...any) Permission { - if err := r.Valid(); err != nil { - return r - } - - for i := 0; i < len(x); i++ { - r.permission.unshift(x[i]) + if err := r.Valid(); err == nil { + for i := 0; i < len(x); i++ { + r.permission.unshift(x[i]) //rights.cast().Unshift(x[i]) + } } return r } @@ -412,7 +294,7 @@ func (r *permission) isZero() bool { return true } - return r.bool == nil && r.Right == nil + return r.bool == nil && r.rights == nil } /* @@ -422,11 +304,10 @@ if processing fails. */ func (r *Permission) Parse(raw string) (err error) { var perm *permission - if perm, err = parsePermission(raw); err != nil { - return + if perm, err = parsePermission(raw); err == nil { + r.permission = perm } - r.permission = perm return } @@ -435,14 +316,12 @@ Valid returns a non-error instance if the receiver fails to pass basic validity checks. */ func (r Permission) Valid() (err error) { - if r.IsZero() { - err = nilInstanceErr(r) - return - } - - err = noPermissionDispErr() - if r.permission.bool != nil { - err = nil + err = nilInstanceErr(r) + if !r.IsZero() { + err = noPermissionDispErr() + if r.permission.bool != nil { + err = nil + } } return diff --git a/rights_test.go b/rights_test.go index ae28d13..ed17fa6 100644 --- a/rights_test.go +++ b/rights_test.go @@ -7,6 +7,8 @@ import ( func TestAllow(t *testing.T) { G := Allow(ReadAccess, CompareAccess) + t.Logf("WHAT THE FUCK %d\n", G.permission.rights.cast().Int()) + want := `allow(read,compare)` got := G.String() if want != got { @@ -189,7 +191,7 @@ func TestRights_bogus(t *testing.T) { func TestRights_lrShift(t *testing.T) { var p Permission = Allow(NoAccess) - if !p.Positive(0) || !p.Positive(`none`) { + if !p.Positive(0) || !p.Positive(`none`) || !p.positive(NoAccess) { t.Errorf("%s failed: cannot identify 'none' permission", t.Name()) return } diff --git a/sec.go b/sec.go index e6c8807..7b54be6 100644 --- a/sec.go +++ b/sec.go @@ -87,6 +87,8 @@ func (r AuthenticationMethod) Eq() BindRule { Ne initializes and returns a new BindRule instance configured to express the evaluation of the receiver value as Not-Equal-To the `authmethod` Bind keyword context. + +Negated equality BindRule instances should be used with caution. */ func (r AuthenticationMethod) Ne() BindRule { if r == noAuth { diff --git a/shifty.go b/shifty.go new file mode 100644 index 0000000..0975747 --- /dev/null +++ b/shifty.go @@ -0,0 +1,55 @@ +package aci + +/* +shifty.go is a bridge to the go-shifty bit-shifting package's types and methods. +*/ + +import ( + "github.com/JesseCoretta/go-shifty" +) + +func newDoW() DayOfWeek { + return DayOfWeek(shifty.New(shifty.Uint8)) +} + +func newLvls() *levels { + l := levels(shifty.New(shifty.Uint16)) + return &l +} + +func newRights() *rights { + r := rights(shifty.New(shifty.Uint16)) + return &r +} + +func (r DayOfWeek) cast() shifty.BitValue { + return shifty.BitValue(r) +} + +func (r levels) cast() shifty.BitValue { + return shifty.BitValue(r) +} + +func (r rights) cast() shifty.BitValue { + return shifty.BitValue(r) +} + +/* +days is the private type for pointer embedding within +instances of DayOfWeek. +*/ +type ( + // DayOfWeek is a type alias of shifty.BitValue, and is used + // to construct a dayofweek BindRule. + DayOfWeek shifty.BitValue // 8-bit + + // rights is a private type alias of shifty.BitValue, and is + // used in the construction an instance of Permission. + rights shifty.BitValue // 16-bit + + // levels is a private type alias of shifty.BitValue, and is + // used in the construction an inheritance-based userattr or + // groupattr BindRule by embedding. + levels shifty.BitValue // 16-bit + +) diff --git a/time.go b/time.go index a927915..d4d678d 100644 --- a/time.go +++ b/time.go @@ -32,37 +32,24 @@ as Sunday (1). */ type Day uint8 -/* -DayOfWeek contains embedded left-shifted bits that collectively represent one or -more days of the week in a "dayofweek" Bind Rule condition. -*/ -type DayOfWeek struct { - *days -} - -/* -days is the private type for pointer embedding within -instances of DayOfWeek. -*/ -type days uint8 - /* iterate a comma-delimited list and verify each slice as a day of the week. return a DayOfWeek instance alongside a Boolean value indicative of success. */ -func parseDoW(d string) (D DayOfWeek, err error) { - X := split(repAll(d, ` `, ``), `,`) +func parseDoW(dow string) (d DayOfWeek, err error) { + d = newDoW() + X := split(repAll(dow, ` `, ``), `,`) for i := 0; i < len(X); i++ { dw := matchStrDoW(X[i]) if dw == noDay { err = dowBadDayErr(X[i]) return } - D.Shift(dw) + d.Shift(dw) } - err = D.Valid() + err = d.Valid() return } @@ -128,8 +115,8 @@ func matchIntDoW(d int) (D Day) { DoW initializes, shifts and returns a new instance of DayOfWeek in one shot. This function an alternative to separate assignment and set procedures. */ -func DoW(x ...any) (D DayOfWeek) { - d := DayOfWeek{new(days)} +func DoW(x ...any) (d DayOfWeek) { + d = newDoW() // assert each dow's type and analyze. // If deemed a valid dow, left-shift @@ -141,13 +128,10 @@ func DoW(x ...any) (D DayOfWeek) { d.Shift(dw) } case Day: - if tv.String() != badDoW { - d.Shift(tv) - } + d.Shift(tv) } } - D.days = d.days return } @@ -163,8 +147,8 @@ would represent an abstract length of two (2). */ func (r DayOfWeek) Len() int { var D int - for i := 0; i < bitSize(noDay); i++ { - if d := Day(1 << i); r.Positive(d) { + for i := 0; i < r.cast().Size(); i++ { + if d := Day(1 << i); r.cast().Positive(d) { D++ } } @@ -182,7 +166,6 @@ Supplying an invalid or nonapplicable ComparisonOperator to this method shall re BindRule instance. */ func Weekdays(cop any) (b BindRule) { - b = badBindRule if c, meth := DoW(Mon, Tues, Wed, Thur, Fri).BRM().index(cop); c.Valid() == nil { b = meth() } @@ -199,7 +182,6 @@ Supplying an invalid or nonapplicable ComparisonOperator to this method shall re BindRule instance. */ func Weekend(cop any) (b BindRule) { - b = badBindRule if c, meth := DoW(Sun, Sat).BRM().index(cop); c.Valid() == nil { b = meth() } @@ -207,47 +189,41 @@ func Weekend(cop any) (b BindRule) { } /* -Shift shifts the first (1st) byte within the receiver instance of DayOfWeek to -include Day x, if not already present. +Shift wraps go-shifty's BitValue.Shift method. */ -func (r *DayOfWeek) Shift(x Day) *DayOfWeek { +func (r *DayOfWeek) Shift(x Day) DayOfWeek { if r.IsZero() { - r.days = new(days) + *r = newDoW() } - - (*r.days) |= days(x) - return r + r.cast().Shift(x) + return *r } /* -Positive returns a Boolean value indicative of whether the receiver instance -of DayOfWeek includes Day x. +Positive wraps go-shifty's BitValue.Positive method. */ -func (r DayOfWeek) Positive(x Day) bool { - if r.IsZero() { - return false +func (r DayOfWeek) Positive(x Day) (posi bool) { + if !r.IsZero() { + posi = r.cast().Positive(x) } - return (*r.days)&days(x) > 0 + return } /* -Unshift right-shifts the first (1st) byte within the receiver instance of DayOfWeek -to remove Day x, if present. +Unshift wraps go-shifty's BitValue.Unshift method. */ -func (r *DayOfWeek) Unshift(x Day) *DayOfWeek { - if r.IsZero() { - return r +func (r *DayOfWeek) Unshift(x Day) DayOfWeek { + if !r.IsZero() { + r.cast().Unshift(x) } - - (*r.days) = (*r.days) &^ days(x) - return r + return *r } /* IsZero returns a Boolean value indicative of whether the receiver is nil, or unset. */ func (r DayOfWeek) IsZero() bool { - return r.days == nil + return r.cast().Kind() == 0x0 } /* @@ -259,9 +235,9 @@ func (r DayOfWeek) String() (s string) { s = badDoW var dows []string - for i := 0; i < bitSize(noDay); i++ { + for i := 0; i < r.cast().Size(); i++ { day := Day(1 << i) - if r.Positive(day) { + if r.cast().Positive(day) { dows = append(dows, day.String()) } } @@ -304,11 +280,11 @@ Eq initializes and returns a new BindRule instance configured to express the evaluation of the receiver value as Equal-To the `dayofweek` Bind keyword context. */ -func (r DayOfWeek) Eq() BindRule { - if err := r.Valid(); err != nil { - return badBindRule +func (r DayOfWeek) Eq() (b BindRule) { + if err := r.Valid(); err == nil { + b = BR(BindDoW, Eq, r) } - return BR(BindDoW, Eq, r) + return } /* @@ -318,11 +294,11 @@ context. Negated equality BindRule instances should be used with caution. */ -func (r DayOfWeek) Ne() BindRule { - if err := r.Valid(); err != nil { - return badBindRule +func (r DayOfWeek) Ne() (b BindRule) { + if err := r.Valid(); err == nil { + b = BR(BindDoW, Ne, r) } - return BR(BindDoW, Ne, r) + return } /* @@ -617,8 +593,6 @@ func (r *timeOfDay) set(t any) { assertToD is called by timeOfDay.set for the purpose of handling a potential clock time value for use in a Bind Rule statement. - -TODO: handle pure int w/o interpolation as binary. */ func assertToD(r *timeOfDay, t any) { switch tv := t.(type) { diff --git a/time_test.go b/time_test.go index 7fe0683..79f019b 100644 --- a/time_test.go +++ b/time_test.go @@ -98,6 +98,13 @@ func ExampleDayOfWeek_Valid() { // Output: Valid: false } +func ExampleTimeOfDay_Set() { + var tod TimeOfDay + tod.Set(`1401`) + fmt.Printf("Time: %s", tod) + // Output: Time: 1401 +} + func ExampleTimeOfDay_BRM() { var tod TimeOfDay fmt.Printf("%d available comparison operator methods", tod.BRM().Len())