diff --git a/element.go b/element.go index ba76aec..e4da48f 100644 --- a/element.go +++ b/element.go @@ -5,6 +5,7 @@ import ( "encoding/xml" "fmt" "io" + "maps" "slices" "strings" ) @@ -171,7 +172,7 @@ func (e *element) writeGoType(w io.Writer, options *generateOptions, indentPrefi if e.root && options.namedRoot { fmt.Fprintf(w, "%s\tXMLName xml.Name `xml:\"%s\"`\n", indentPrefix, e.name.Local) } - for _, exportedAttrName := range sortedKeys(attrValuesByExportedName) { + for _, exportedAttrName := range slices.Sorted(maps.Keys(attrValuesByExportedName)) { attrValue := attrValuesByExportedName[exportedAttrName] fmt.Fprintf(w, "%s\t%s %s `xml:\"%s,attr\"`\n", indentPrefix, exportedAttrName, attrValue.goType(options), attrValue.name.Local) } @@ -185,7 +186,7 @@ func (e *element) writeGoType(w io.Writer, options *generateOptions, indentPrefi fmt.Fprintf(w, "%s\t%s string `xml:\",chardata\"`\n", indentPrefix, fieldName) } - childElements := mapValues(e.childElements) + childElements := slices.Collect(maps.Values(e.childElements)) if options.preserveOrder { slices.SortFunc(childElements, func(a, b *element) int { return e.childOrder[a.name] - e.childOrder[b.name] diff --git a/generator.go b/generator.go index 4125ef8..85072a2 100644 --- a/generator.go +++ b/generator.go @@ -7,9 +7,9 @@ import ( "go/format" "io" "io/fs" + "maps" "os" "slices" - "sort" "strings" "golang.org/x/net/html/charset" @@ -306,9 +306,9 @@ func (g *Generator) Generate() ([]byte, error) { options.simpleTypes[name] = struct{}{} delete(options.namedTypes, name) } - typeElements = mapValues(options.namedTypes) + typeElements = slices.Collect(maps.Values(options.namedTypes)) } else { - typeElements = mapValues(g.typeElements) + typeElements = slices.Collect(maps.Values(g.typeElements)) } if options.preserveOrder { @@ -365,9 +365,7 @@ func (g *Generator) Generate() ([]byte, error) { } default: fmt.Fprintf(sourceBuilder, "import (\n") - importPackageNames := mapKeys(options.importPackageNames) - sort.Strings(importPackageNames) - for _, importPackageName := range importPackageNames { + for _, importPackageName := range slices.Sorted(maps.Keys(options.importPackageNames)) { fmt.Fprintf(sourceBuilder, "\t%q\n", importPackageName) } fmt.Fprintf(sourceBuilder, ")\n") diff --git a/xmlstruct.go b/xmlstruct.go index 4654b39..141c447 100644 --- a/xmlstruct.go +++ b/xmlstruct.go @@ -2,10 +2,8 @@ package xmlstruct import ( - "cmp" "encoding/xml" "regexp" - "slices" "strings" "unicode" ) @@ -31,69 +29,9 @@ const ( ) var ( - // TitleFirstRuneExportNameFunc returns name.Local with the initial rune - // capitalized. - TitleFirstRuneExportNameFunc = func(name xml.Name) string { - runes := []rune(name.Local) - runes[0] = unicode.ToUpper(runes[0]) - return string(runes) - } - kebabOrSnakeCaseWordBoundaryRx = regexp.MustCompile(`[-_]+\pL`) nonIdentifierRuneRx = regexp.MustCompile(`[^\pL\pN]`) - // DefaultExportNameFunc returns name.Local with kebab- and snakecase words - // converted to UpperCamelCase and any Id suffix converted to ID. - DefaultExportNameFunc = func(name xml.Name) string { - localName := kebabOrSnakeCaseWordBoundaryRx.ReplaceAllStringFunc(name.Local, func(s string) string { - return strings.ToUpper(s[len(s)-1:]) - }) - localName = nonIdentifierRuneRx.ReplaceAllLiteralString(localName, "_") - runes := []rune(localName) - runes[0] = unicode.ToUpper(runes[0]) - if len(runes) > 1 && runes[len(runes)-2] == 'I' && runes[len(runes)-1] == 'd' { - runes[len(runes)-1] = 'D' - } - return string(runes) - } - - // DefaultUnexportNameFunc returns name.Local with kebab- and snakecase words - // converted to lowerCamelCase - // Any ID prefix is converted to id, and any Id suffix converted to ID. - DefaultUnexportNameFunc = func(name xml.Name) string { - localName := kebabOrSnakeCaseWordBoundaryRx.ReplaceAllStringFunc(name.Local, func(s string) string { - return strings.ToUpper(s[len(s)-1:]) - }) - localName = nonIdentifierRuneRx.ReplaceAllLiteralString(localName, "_") - runes := []rune(localName) - runes[0] = unicode.ToLower(runes[0]) - if len(runes) > 1 { - if runes[len(runes)-2] == 'I' && runes[len(runes)-1] == 'd' { - runes[len(runes)-1] = 'D' - } - if runes[0] == 'i' && runes[1] == 'D' { - runes[1] = 'd' - } - } - return string(runes) - } -) - -var ( - // IgnoreNamespaceNameFunc returns name with name.Space cleared. The same - // local name in different namespaces will be treated as identical names. - IgnoreNamespaceNameFunc = func(name xml.Name) xml.Name { - return xml.Name{ - Local: name.Local, - } - } - - // The IdentityNameFunc returns name unchanged. The same local name in - // different namespaces will be treated as distinct names. - IdentityNameFunc = func(name xml.Name) xml.Name { - return name - } - DefaultNameFunc = IgnoreNamespaceNameFunc ) @@ -134,25 +72,60 @@ type generateOptions struct { emptyElements bool } -func mapKeys[M ~map[K]V, K comparable, V any](m M) []K { - keys := make([]K, 0, len(m)) - for k := range m { - keys = append(keys, k) +// DefaultExportNameFunc returns name.Local with kebab- and snake_case words +// converted to UpperCamelCase and any Id suffix converted to ID. +func DefaultExportNameFunc(name xml.Name) string { + localName := kebabOrSnakeCaseWordBoundaryRx.ReplaceAllStringFunc(name.Local, func(s string) string { + return strings.ToUpper(s[len(s)-1:]) + }) + localName = nonIdentifierRuneRx.ReplaceAllLiteralString(localName, "_") + runes := []rune(localName) + runes[0] = unicode.ToUpper(runes[0]) + if len(runes) > 1 && runes[len(runes)-2] == 'I' && runes[len(runes)-1] == 'd' { + runes[len(runes)-1] = 'D' + } + return string(runes) +} + +// DefaultUnexportNameFunc returns name.Local with kebab- and snake_case words +// converted to lowerCamelCase. Any ID prefix is converted to id, and any Id +// suffix converted to ID. +func DefaultUnexportNameFunc(name xml.Name) string { + localName := kebabOrSnakeCaseWordBoundaryRx.ReplaceAllStringFunc(name.Local, func(s string) string { + return strings.ToUpper(s[len(s)-1:]) + }) + localName = nonIdentifierRuneRx.ReplaceAllLiteralString(localName, "_") + runes := []rune(localName) + runes[0] = unicode.ToLower(runes[0]) + if len(runes) > 1 { + if runes[len(runes)-2] == 'I' && runes[len(runes)-1] == 'd' { + runes[len(runes)-1] = 'D' + } + if runes[0] == 'i' && runes[1] == 'D' { + runes[1] = 'd' + } } - return keys + return string(runes) } -func mapValues[M ~map[K]V, K comparable, V any](m M) []V { - values := make([]V, 0, len(m)) - for _, v := range m { - values = append(values, v) +// IgnoreNamespaceNameFunc returns name with name.Space cleared. The same local +// name in different namespaces will be treated as identical names. +func IgnoreNamespaceNameFunc(name xml.Name) xml.Name { + return xml.Name{ + Local: name.Local, } - return values } -// sortedKeys returns the keys of m in order. -func sortedKeys[M ~map[K]V, K cmp.Ordered, V any](m M) []K { - keys := mapKeys(m) - slices.Sort(keys) - return keys +// The IdentityNameFunc returns name unchanged. The same local name in different +// namespaces will be treated as distinct names. +func IdentityNameFunc(name xml.Name) xml.Name { + return name +} + +// TitleFirstRuneExportNameFunc returns name.Local with the initial rune +// capitalized. +func TitleFirstRuneExportNameFunc(name xml.Name) string { + runes := []rune(name.Local) + runes[0] = unicode.ToUpper(runes[0]) + return string(runes) }