Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/stretchr/testify v1.11.1
github.com/urfave/cli v1.22.17
golang.org/x/net v0.55.0
golang.org/x/tools v0.45.0
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
6 changes: 3 additions & 3 deletions pkg/xsd/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ func (ext *Extension) Elements() []Element {
goNames[attr.GoName()] = struct{}{}
}

final := []Element{}
for _, element := range elements {
final := make([]Element, len(elements))
for i, element := range elements {
if _, found := goNames[element.GoFieldName()]; found {
element.FieldOverride = true
}
goNames[element.GoFieldName()] = struct{}{}
final = append(final, element)
final[i] = element
}
return final
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/xsd/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"io"
"os"
"path/filepath"
"sort"
"slices"
"strings"

"golang.org/x/net/html/charset"
Expand Down Expand Up @@ -312,7 +312,7 @@ func (sch *Schema) GoImportsNeeded() []string {
for _, importedMod := range sch.importedModules {
imports = append(imports, fmt.Sprintf("%s/%s", sch.ModulesPath, importedMod.GoPackageName()))
}
sort.Strings(imports)
slices.Sort(imports)
return imports
}

Expand Down
45 changes: 36 additions & 9 deletions pkg/xsd/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ import (

type Workspace struct {
Cache map[string]*Schema // Parsed XSD schemas by its filename (user specifies initial one, and we load dependencies)
Loaded map[string]*Schema // Parsed AND resolved schemas by its filename
GoModulesPath string // user requested go package path (example: github.com/gocomply/scap)
xmlnsOverrides xmlnsOverrides // user-supplied xmlns overrides
}

func NewWorkspace(goModulesPath, xsdPath string, xmlnsOverrides []string) (*Workspace, error) {
ws := Workspace{
Cache: map[string]*Schema{},
Loaded: map[string]*Schema{},
GoModulesPath: goModulesPath,
}
var err error
Expand All @@ -29,11 +31,34 @@ func NewWorkspace(goModulesPath, xsdPath string, xmlnsOverrides []string) (*Work
return &ws, ws.compile()
}

// merges unique elements of newer into origin, compared by getName.
func merge[T any, M comparable](newer, origin []T, getName func(T) M) []T {
names := make(map[M]struct{})
for _, o := range origin {
names[getName(o)] = struct{}{}
}

for _, n := range newer {
name := getName(n)
if _, ok := names[name]; ok {
continue
}
origin = append(origin, n)
names[name] = struct{}{}
}
return origin
}

func (ws *Workspace) loadXsd(xsdPath string, shouldBeInlined bool) (*Schema, error) {
cached, found := ws.Cache[xsdPath]
if found {
xsdPath = filepath.Clean(xsdPath)

if schema, found := ws.Loaded[xsdPath]; found {
return schema, nil
}
if cached, found := ws.Cache[xsdPath]; found {
return cached, nil
}

fmt.Println("\tParsing:", xsdPath)

schema, err := ReadSchemaFromFile(xsdPath)
Expand All @@ -45,6 +70,8 @@ func (ws *Workspace) loadXsd(xsdPath string, shouldBeInlined bool) (*Schema, err
schema.filePath = xsdPath
schema.goPackageNameOverride = ws.xmlnsOverrides.override(schema.TargetNamespace)

ws.Loaded[xsdPath] = schema

if !shouldBeInlined {
// Cache all loaded schemas in the workspace, unless it was brought in by xsd:include element.
// Unlike xsd:import, xsd:include does not result in a separate schema in the workspace.
Expand All @@ -60,13 +87,13 @@ func (ws *Workspace) loadXsd(xsdPath string, shouldBeInlined bool) (*Schema, err
}

isch := si.IncludedSchema
schema.Imports = append(isch.Imports, schema.Imports...)
schema.Elements = append(isch.Elements, schema.Elements...)
schema.Attributes = append(isch.Attributes, schema.Attributes...)
schema.AttributeGroups = append(isch.AttributeGroups, schema.AttributeGroups...)
schema.ComplexTypes = append(isch.ComplexTypes, schema.ComplexTypes...)
schema.SimpleTypes = append(isch.SimpleTypes, schema.SimpleTypes...)
schema.inlinedElements = append(isch.inlinedElements, schema.inlinedElements...)
schema.Imports = merge(isch.Imports, schema.Imports, func(i Import) string { return i.Namespace + i.SchemaLocation })
schema.Elements = merge(isch.Elements, schema.Elements, func(e Element) string { return e.Name })
schema.Attributes = merge(isch.Attributes, schema.Attributes, func(a Attribute) string { return a.Name })
schema.AttributeGroups = merge(isch.AttributeGroups, schema.AttributeGroups, func(ag AttributeGroup) string { return ag.Name })
schema.ComplexTypes = merge(isch.ComplexTypes, schema.ComplexTypes, func(ct ComplexType) string { return ct.Name })
schema.SimpleTypes = merge(isch.SimpleTypes, schema.SimpleTypes, func(st SimpleType) string { return st.Name })
schema.inlinedElements = merge(isch.inlinedElements, schema.inlinedElements, func(e Element) string { return e.Name })
for key, sch := range isch.importedModules {
schema.importedModules[key] = sch
}
Expand Down
57 changes: 57 additions & 0 deletions tests/smoke_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/gocomply/xsd2go/pkg/xsd2go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/tools/txtar"
)

func TestSanity(t *testing.T) {
Expand Down Expand Up @@ -58,3 +59,59 @@ func locateGeneratedFile(outputDir string) (string, error) {
}
return golangFiles[0], nil
}

func extractTxtar(t *testing.T, name string) (dir string) {
t.Helper()

data, err := os.ReadFile(name)
require.NoError(t, err)

ar := txtar.Parse(data)

dir = os.TempDir()

for _, f := range ar.Files {
path := filepath.Join(dir, f.Name)

require.NoError(t,
os.MkdirAll(filepath.Dir(path), 0755))

require.NoError(t,
os.WriteFile(path, f.Data, 0600))
}

return
}

func TestCircularImport(t *testing.T) {
workdir, err := os.Getwd()
require.NoError(t, err)

xsdFiles, err := filepath.Glob("xsd-examples/modules/*.txtar")
require.NoError(t, err)
assert.NotEmpty(t, xsdFiles)

for _, xsdPath := range xsdFiles {
dir := extractTxtar(t, filepath.Join(workdir, xsdPath))
t.Chdir(dir)

data, err := os.ReadFile(filepath.Join(workdir, xsdPath+".out"))
require.NoError(t, err)
expectar := txtar.Parse(data)

err = xsd2go.Convert(
"a.xsd",
"example.com/test",
"out",
nil,
)
require.NoError(t, err)

for _, expect := range expectar.Files {
got, err := os.ReadFile(filepath.Join(dir, "out", expect.Name))
require.NoError(t, err)

assert.Equal(t, strings.ReplaceAll(string(expect.Data), "\r\n", "\n"), string(got))
}
}
}
33 changes: 33 additions & 0 deletions tests/xsd-examples/modules/circulair-import.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- a.xsd --
<?xml version="1.0"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:a="urn:a"
xmlns:b="urn:b"
targetNamespace="urn:a">

<xsd:import namespace="urn:b" schemaLocation="b.xsd"/>

<xsd:complexType name="AType">
<xsd:sequence>
<xsd:element name="b" type="b:BType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>

-- b.xsd --
<?xml version="1.0"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:a="urn:a"
xmlns:b="urn:b"
targetNamespace="urn:b">

<xsd:import namespace="urn:a" schemaLocation="a.xsd"/>

<xsd:complexType name="BType">
<xsd:sequence>
<xsd:element name="a" type="a:AType"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
36 changes: 36 additions & 0 deletions tests/xsd-examples/modules/circulair-import.txtar.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- a/models.go --
// Code generated by https://github.com/gocomply/xsd2go; DO NOT EDIT.
// Models for urn:a
package a

import (
"encoding/xml"
"example.com/test/out/b"
)

// XSD ComplexType declarations

type Atype struct {
XMLName xml.Name
B b.Btype `xml:"b"`
}

// XSD SimpleType declarations
-- b/models.go --
// Code generated by https://github.com/gocomply/xsd2go; DO NOT EDIT.
// Models for urn:b
package b

import (
"encoding/xml"
"example.com/test/out/a"
)

// XSD ComplexType declarations

type Btype struct {
XMLName xml.Name
A a.Atype `xml:"a"`
}

// XSD SimpleType declarations
21 changes: 21 additions & 0 deletions tests/xsd-examples/modules/circulair-include.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
-- a.xsd --
<?xml version="1.0"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:test">

<xsd:include schemaLocation="b.xsd"/>

<xsd:complexType name="AType"/>
</xsd:schema>

-- b.xsd --
<?xml version="1.0"?>
<xsd:schema
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:test">

<xsd:include schemaLocation="a.xsd"/>

<xsd:complexType name="BType"/>
</xsd:schema>
20 changes: 20 additions & 0 deletions tests/xsd-examples/modules/circulair-include.txtar.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- a/models.go --
// Code generated by https://github.com/gocomply/xsd2go; DO NOT EDIT.
// Models for urn:test
package a

import (
"encoding/xml"
)

// XSD ComplexType declarations

type Atype struct {
XMLName xml.Name
}

type Btype struct {
XMLName xml.Name
}

// XSD SimpleType declarations
27 changes: 27 additions & 0 deletions vendor/golang.org/x/tools/LICENSE

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions vendor/golang.org/x/tools/PATENTS

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading