Skip to content
This repository has been archived by the owner on Dec 1, 2024. It is now read-only.

Commit

Permalink
preliminary marshal support
Browse files Browse the repository at this point in the history
  • Loading branch information
Jesse Coretta authored and Jesse Coretta committed Nov 9, 2024
1 parent 4317fce commit aeb5320
Show file tree
Hide file tree
Showing 19 changed files with 1,033 additions and 21 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# go-schemax

Package schemax incorporates a powerful [RFC 4512](https://www.rfc-editor.org/rfc/rfc4512.txt) parser, wrapped with convenient, reflective features for creating and interrogating directory schemas.
Package schemax (_`skee·muh·eks`_) incorporates a powerful [RFC 4512](https://www.rfc-editor.org/rfc/rfc4512.txt) parser, wrapped with convenient, reflective features for creating and interrogating directory schemas.

Requires Go version 1.22 or higher.

Expand Down Expand Up @@ -110,6 +110,10 @@ The general rule-of-thumb is suggests that if the `ls -l` Bash command _consiste

The `ParseRaw` method is subject to the same conditions related to the order of dependent definitions.

## Marshal support

When needed, all `Definition` qualifier types allow for convenient population by way of an instance of `DefinitionMap` or `map[string]any` being submitted to the appropriate `Marshal` method held by the desired receiver instance. This feature bridges the gap between other markdown languages, such as JSON, and allows easy conversion into the desired definition type.

## The Schema Itself

The `Schema` type defined within this package is a [`stackage.Stack`](https://pkg.go.dev/github.com/JesseCoretta/go-stackage#Stack) derivative type. An instance of a `Schema` can manifest in any of the following manners:
Expand Down
123 changes: 109 additions & 14 deletions at.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,107 @@ func newAttributeType() *attributeType {
}
}

/*
Marshal returns an error following an attempt to marshal the contents of
def, which may be either a [DefinitionMap] or map[string]any instance.
The receiver instance must be initialized prior to use of this method
using the [Schema.AttributeType] method.
*/
func (r AttributeType) Marshal(def any) error {
m, err := getMarshalMap(r, def)
if err != nil {
return err
}

for k, v := range m {
switch key := uc(k); key {
case `NAME`:
switch tv := v.(type) {
case string:
r.SetName(tv)
case []string:
r.SetName(tv...)
}
case `NUMERICOID`, `DESC`:
z := map[string]func(string) AttributeType{
`DESC`: r.SetDescription,
`NUMERICOID`: r.SetNumericOID,
}
switch tv := v.(type) {
case string:
z[k](tv)
case []string:
z[k](tv[0])
}
case `OBSOLETE`, `COLLECTIVE`,
`NO-USER-MODIFICATION`, `SINGLE-VALUE`:
z := map[string]func() AttributeType{
`OBSOLETE`: r.SetObsolete,
`NO-USER-MODIFICATION`: r.SetNoUserModification,
`COLLECTIVE`: r.SetCollective,
`SINGLE-VALUE`: r.SetSingleValue,
}

r.marshalBoolean(v, z[k])
case `EQUALITY`, `SYNTAX`, `SUBSTRING`, `SUP`,
`ORDERING`, `USAGE`, `SUBSTR`:
z := map[string]func(any) AttributeType{
`SUP`: r.SetSuperType,
`SYNTAX`: r.SetSyntax,
`EQUALITY`: r.SetEquality,
`ORDERING`: r.SetOrdering,
`SUBSTR`: r.SetSubstring,
`SUBSTRING`: r.SetSubstring,
`USAGE`: r.SetUsage,
}
switch tv := v.(type) {
case string:
z[k](tv)
case []string:
z[k](tv[0])
}
default:
r.marshalExt(key, v)
}
}

if !r.Compliant() {
return ErrDefNonCompliant
}
r.SetStringer()

return nil
}

func (r AttributeType) marshalExt(key string, v any) {
if hasPfx(key, `X-`) {
switch tv := v.(type) {
case string:
r.SetExtension(key, tv)
case []string:
r.SetExtension(key, tv...)
}
}
}

func (r AttributeType) marshalBoolean(v any, funk func() AttributeType) {
switch tv := v.(type) {
case string:
if eq(tv, `TRUE`) {
funk()
}
case []string:
if eq(tv[0], `TRUE`) {
funk()
}
case bool:
if tv {
funk()
}
}
}

/*
Parse returns an error following an attempt to parse raw into the receiver
instance.
Expand Down Expand Up @@ -1452,9 +1553,9 @@ Note that a value of true will be ignored if the receiver is a collective
This is a fluent method.
*/
func (r AttributeType) SetSingleValue(x any) AttributeType {
func (r AttributeType) SetSingleValue() AttributeType {
if !r.IsZero() {
r.attributeType.setBoolean(`sv`, x)
r.attributeType.setBoolean(`sv`, true)
}

return r
Expand All @@ -1472,9 +1573,9 @@ Note that a value of true will be ignored if the receiver is a single-valued
This is a fluent method.
*/
func (r AttributeType) SetCollective(x any) AttributeType {
func (r AttributeType) SetCollective() AttributeType {
if !r.IsZero() {
r.attributeType.setBoolean(`c`, x)
r.attributeType.setBoolean(`c`, true)
}

return r
Expand All @@ -1489,9 +1590,9 @@ are used, case is not significant.
This is a fluent method.
*/
func (r AttributeType) SetNoUserModification(x any) AttributeType {
func (r AttributeType) SetNoUserModification() AttributeType {
if !r.IsZero() {
r.attributeType.setBoolean(`num`, x)
r.attributeType.setBoolean(`num`, true)
}

return r
Expand All @@ -1504,20 +1605,14 @@ Obsolescence cannot be unset.
This is a fluent method.
*/
func (r AttributeType) SetObsolete(x any) AttributeType {
func (r AttributeType) SetObsolete() AttributeType {
if !r.IsZero() {
r.attributeType.setObsolete()
r.attributeType.setBoolean(`obs`, true)
}

return r
}

func (r *attributeType) setObsolete() {
if !r.Obsolete {
r.Obsolete = true
}
}

func (r *attributeType) setBoolean(t string, x any) {

var Bool bool
Expand Down
53 changes: 47 additions & 6 deletions at_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,47 @@ import (
"unicode/utf8"
)

/*
This example demonstrates the means for marshaling an instance of
[AttributeType] from a map[string]any instance.
*/
func ExampleAttributeType_Marshal() {
m := map[string]any{
`NAME`: `exampleAttributeType`,
`DESC`: `This is an example`,
`NUMERICOID`: `1.3.6.1.4.1.56521.999.12.34.56`,
`COLLECTIVE`: `FALSE`,
`NO-USER-MODIFICATION`: `TRUE`,
`SINGLE-VALUE`: `TRUE`,
`OBSOLETE`: `FALSE`,
`EQUALITY`: `integerMatch`,
`ORDERING`: `integerOrderingMatch`,
`SUBSTR`: `caseIgnoreSubstringsMatch`,
`SYNTAX`: `1.3.6.1.4.1.1466.115.121.1.27`,
`USAGE`: `dSAOperation`,
`X-ORIGIN`: `RFCXXXX`,
}

var def AttributeType = mySchema.NewAttributeType()
if err := def.Marshal(m); err != nil {
fmt.Println(err)
return
}

fmt.Printf("%s\n", def)
// Output: ( 1.3.6.1.4.1.56521.999.12.34.56
// NAME 'exampleAttributeType'
// DESC 'This is an example'
// EQUALITY integerMatch
// SUBSTR caseIgnoreSubstringsMatch
// ORDERING integerOrderingMatch
// SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
// SINGLE-VALUE
// NO-USER-MODIFICATION
// USAGE dSAOperation
// X-ORIGIN 'RFCXXXX' )
}

/*
This example demonstrates the means of gathering references to every
superior [AttributeType] in the relevant super type chain.
Expand Down Expand Up @@ -285,7 +326,7 @@ func ExampleNewAttributeType() {
SetSyntax(dStr).
SetMinimumUpperBounds(64).
SetEquality(cIM).
SetSingleValue(true).
SetSingleValue().
SetExtension(`X-ORIGIN`, `NOWHERE`).
SetStringer() // use default stringer

Expand Down Expand Up @@ -1053,11 +1094,11 @@ func TestAttributeType_codecov(t *testing.T) {
}
zz.superChain()
zz.setSuperType(mySchema.AttributeTypes().Get(`cn`))
zz.SetCollective(rune(88))
zz.SetCollective(true)
zz.SetObsolete(true)
zz.SetObsolete(`true`)
zz.SetNoUserModification(true)
zz.SetCollective()
zz.SetCollective()
zz.SetObsolete()
zz.SetObsolete()
zz.SetNoUserModification()
zz.SetUsage(`directoryOperation`)
zz.Usage()
zz.SetUsage(`distributedOperation`)
Expand Down
95 changes: 95 additions & 0 deletions dc.go
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,101 @@ func (r DITContentRule) mayComply(must, may AttributeTypes) bool {
return true
}

/*
Marshal returns an error following an attempt to marshal the contents of
def, which may be either a [DefinitionMap] or map[string]any instance.
The receiver instance must be initialized prior to use of this method
using the [Schema.NewDITContentRule] method.
*/
func (r DITContentRule) Marshal(def any) error {
m, err := getMarshalMap(r, def)
if err != nil {
return err
}

for k, v := range m {
switch key := uc(k); key {
case `NAME`:
switch tv := v.(type) {
case string:
r.SetName(tv)
case []string:
r.SetName(tv...)
}
case `NUMERICOID`, `DESC`:
z := map[string]func(string) DITContentRule{
`DESC`: r.SetDescription,
`NUMERICOID`: r.SetNumericOID,
}
switch tv := v.(type) {
case string:
z[k](tv)
case []string:
z[k](tv[0])
}
case `OBSOLETE`:
r.marshalBoolean(v)
case `MUST`, `MAY`, `NOT`, `AUX`:
r.marshalMulti(key, v)
default:
r.marshalExt(key, v)
}
}

if !r.Compliant() {
return ErrDefNonCompliant
}
r.SetStringer()

return nil
}

func (r DITContentRule) marshalBoolean(v any) {
switch tv := v.(type) {
case string:
if eq(tv, `TRUE`) {
r.SetObsolete()
}
case []string:
if eq(tv[0], `TRUE`) {
r.SetObsolete()
}
case bool:
if tv {
r.SetObsolete()
}
}
}

func (r DITContentRule) marshalExt(key string, v any) {
if hasPfx(key, `X-`) {
switch tv := v.(type) {
case string:
r.SetExtension(key, tv)
case []string:
r.SetExtension(key, tv...)
}
}
}

func (r DITContentRule) marshalMulti(k string, v any) {
z := map[string]func(...any) DITContentRule{
`MUST`: r.SetMust,
`MAY`: r.SetMay,
`NOT`: r.SetNot,
`AUX`: r.SetAux,
}
switch tv := v.(type) {
case []string:
for i := 0; i < len(tv); i++ {
z[k](tv[i])
}
case string:
z[k](tv)
}
}

/*
Parse returns an error following an attempt to parse raw into the receiver
instance.
Expand Down
Loading

0 comments on commit aeb5320

Please sign in to comment.