Skip to content
Merged
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
32 changes: 32 additions & 0 deletions pkg/gen/filters/filtercs/cs_async_return.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package filtercs

import (
"fmt"

"github.com/apigear-io/cli/pkg/model"
)

// ToAsyncReturnString wraps the C# return type in a System.Threading.Tasks.Task.
// A void return becomes a non-generic Task; everything else becomes Task<T>.
// Unlike Java's CompletableFuture, C# generics accept value types directly, so
// no boxing of primitives is required.
func ToAsyncReturnString(prefix string, schema *model.Schema) (string, error) {
if schema == nil {
return "xxx", fmt.Errorf("ToAsyncReturnString schema is nil")
}
if schema.KindType == model.TypeVoid && !schema.IsArray {
return "Task", nil
}
inner, err := ToReturnString(prefix, schema)
if err != nil {
return "xxx", fmt.Errorf("csAsyncReturn type error: %s", err)
}
return fmt.Sprintf("Task<%s>", inner), nil
}

func csAsyncReturn(prefix string, node *model.TypedNode) (string, error) {
if node == nil {
return "xxx", fmt.Errorf("csAsyncReturn node is nil")
}
return ToAsyncReturnString(prefix, &node.Schema)
}
70 changes: 70 additions & 0 deletions pkg/gen/filters/filtercs/cs_async_return_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package filtercs

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestAsyncReturn(t *testing.T) {
t.Parallel()
syss := loadTestSystems(t)
var propTests = []struct {
mn string
in string
pn string
rt string
}{
{"test", "Test3", "opVoid", "Task"},
{"test", "Test3", "opBool", "Task<bool>"},
{"test", "Test3", "opInt", "Task<int>"},
{"test", "Test3", "opInt32", "Task<int>"},
{"test", "Test3", "opInt64", "Task<long>"},
{"test", "Test3", "opFloat", "Task<float>"},
{"test", "Test3", "opFloat64", "Task<double>"},
{"test", "Test3", "opString", "Task<string>"},
{"test", "Test3", "opBoolArray", "Task<List<bool>>"},
{"test", "Test3", "opIntArray", "Task<List<int>>"},
{"test", "Test3", "opStringArray", "Task<List<string>>"},
}
for _, sys := range syss {
for _, tt := range propTests {
t.Run(tt.pn, func(t *testing.T) {
op := sys.LookupOperation(tt.mn, tt.in, tt.pn)
assert.NotNil(t, op)
r, err := csAsyncReturn("", op.Return)
assert.NoError(t, err)
assert.Equal(t, tt.rt, r)
})
}
}
}

func TestAsyncReturnSymbols(t *testing.T) {
t.Parallel()
syss := loadTestSystems(t)
var propTests = []struct {
mn string
in string
pn string
rt string
}{
{"test", "Test4", "opEnum", "Task<Enum1>"},
{"test", "Test4", "opStruct", "Task<Struct1>"},
{"test", "Test4", "opInterface", "Task<IInterface1>"},
{"test", "Test4", "opEnumArray", "Task<List<Enum1>>"},
{"test", "Test4", "opStructArray", "Task<List<Struct1>>"},
{"test", "Test4", "opInterfaceArray", "Task<List<IInterface1>>"},
}
for _, sys := range syss {
for _, tt := range propTests {
t.Run(tt.pn, func(t *testing.T) {
op := sys.LookupOperation(tt.mn, tt.in, tt.pn)
assert.NotNil(t, op)
r, err := csAsyncReturn("", op.Return)
assert.NoError(t, err)
assert.Equal(t, tt.rt, r)
})
}
}
}
86 changes: 86 additions & 0 deletions pkg/gen/filters/filtercs/cs_default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package filtercs

import (
"fmt"

"github.com/apigear-io/cli/pkg/gen/filters/common"
"github.com/apigear-io/cli/pkg/model"
)

// ToDefaultString returns the C# default value for a schema. Arrays default to
// an empty List<T>; strings to string.Empty; byte arrays to Array.Empty<byte>().
func ToDefaultString(prefix string, schema *model.Schema) (string, error) {
if schema == nil {
return "xxx", fmt.Errorf("ToDefaultString schema is nil")
}
if schema.IsArray {
inner := schema.InnerSchema()
ret, err := ToReturnString(prefix, &inner)
if err != nil {
return "xxx", fmt.Errorf("csDefault inner value error: %s", err)
}
return fmt.Sprintf("new List<%s>()", ret), nil
}
text := ""
switch schema.KindType {
case model.TypeVoid:
text = "void"
case model.TypeBool:
text = "false"
case model.TypeInt, model.TypeInt32:
text = "0"
case model.TypeInt64:
text = "0L"
case model.TypeFloat, model.TypeFloat32:
text = "0.0f"
case model.TypeFloat64:
text = "0.0"
case model.TypeString:
text = "string.Empty"
case model.TypeBytes:
text = "System.Array.Empty<byte>()"
case model.TypeAny:
text = "null"
case model.TypeExtern:
xe := parseCsExtern(schema)
if xe.Default != "" {
text = xe.Default
} else {
ns := ""
if xe.Namespace != "" {
ns = fmt.Sprintf("%s.", xe.Namespace)
}
text = fmt.Sprintf("new %s%s()", ns, xe.Name)
}
case model.TypeEnum:
e := schema.LookupEnum(schema.Import, schema.Type)
if e == nil {
return "xxx", fmt.Errorf("csDefault enum not found: %s", schema.Dump())
}
if schema.Import != "" {
prefix = fmt.Sprintf("%s.%s.", common.CamelTitleCase(schema.System().Name), common.CamelTitleCase(schema.Import))
}
text = fmt.Sprintf("%s%s.%s", prefix, common.CamelTitleCase(e.Name), common.CamelTitleCase(e.Members[0].Name))
case model.TypeStruct:
s := schema.LookupStruct(schema.Import, schema.Type)
if s == nil {
return "xxx", fmt.Errorf("csDefault struct not found: %s", schema.Dump())
}
if schema.Import != "" {
prefix = fmt.Sprintf("%s.%s.", common.CamelTitleCase(schema.System().Name), common.CamelTitleCase(schema.Import))
}
text = fmt.Sprintf("new %s%s()", prefix, common.CamelTitleCase(s.Name))
case model.TypeInterface:
text = "null"
default:
return "xxx", fmt.Errorf("csDefault unknown schema %s", schema.Dump())
}
return text, nil
}

func csDefault(prefix string, node *model.TypedNode) (string, error) {
if node == nil {
return "xxx", fmt.Errorf("csDefault node is nil")
}
return ToDefaultString(prefix, &node.Schema)
}
73 changes: 73 additions & 0 deletions pkg/gen/filters/filtercs/cs_default_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package filtercs

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestDefault(t *testing.T) {
t.Parallel()
syss := loadTestSystems(t)
var propTests = []struct {
mn string
in string
pn string
rt string
}{
{"test", "Test1", "propBool", "false"},
{"test", "Test1", "propInt", "0"},
{"test", "Test1", "propInt32", "0"},
{"test", "Test1", "propInt64", "0L"},
{"test", "Test1", "propFloat", "0.0f"},
{"test", "Test1", "propFloat32", "0.0f"},
{"test", "Test1", "propFloat64", "0.0"},
{"test", "Test1", "propString", "string.Empty"},
{"test", "Test1", "propBytes", "System.Array.Empty<byte>()"},
{"test", "Test1", "propAny", "null"},
{"test", "Test1", "propBoolArray", "new List<bool>()"},
{"test", "Test1", "propIntArray", "new List<int>()"},
{"test", "Test1", "propInt64Array", "new List<long>()"},
{"test", "Test1", "propStringArray", "new List<string>()"},
}
for _, sys := range syss {
for _, tt := range propTests {
t.Run(tt.pn, func(t *testing.T) {
prop := sys.LookupProperty(tt.mn, tt.in, tt.pn)
assert.NotNil(t, prop)
r, err := csDefault("", prop)
assert.NoError(t, err)
assert.Equal(t, tt.rt, r)
})
}
}
}

func TestDefaultSymbols(t *testing.T) {
t.Parallel()
syss := loadTestSystems(t)
var propTests = []struct {
mn string
in string
pn string
rt string
}{
{"test", "Test2", "propEnum", "Enum1.Default"},
{"test", "Test2", "propStruct", "new Struct1()"},
{"test", "Test2", "propInterface", "null"},
{"test", "Test2", "propEnumArray", "new List<Enum1>()"},
{"test", "Test2", "propStructArray", "new List<Struct1>()"},
{"test", "Test2", "propInterfaceArray", "new List<IInterface1>()"},
}
for _, sys := range syss {
for _, tt := range propTests {
t.Run(tt.pn, func(t *testing.T) {
prop := sys.LookupProperty(tt.mn, tt.in, tt.pn)
assert.NotNil(t, prop)
r, err := csDefault("", prop)
assert.NoError(t, err)
assert.Equal(t, tt.rt, r)
})
}
}
}
46 changes: 46 additions & 0 deletions pkg/gen/filters/filtercs/cs_ns.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package filtercs

import (
"fmt"
"strings"

"github.com/apigear-io/cli/pkg/gen/filters/common"
"github.com/apigear-io/cli/pkg/model"
)

// nsName builds the dotted, PascalCased C# namespace for a module
// (e.g. module "demo.x" -> "Demo.X").
func nsName(m *model.Module) (string, error) {
if m == nil {
return "", fmt.Errorf("invalid module")
}
parts := strings.Split(m.Name, ".")
for i, p := range parts {
parts[i] = common.CamelTitleCase(p)
}
return strings.Join(parts, "."), nil
}

// csNs returns the C# namespace name for a module.
func csNs(m *model.Module) (string, error) {
return nsName(m)
}

// csNsOpen opens a block-scoped C# namespace. Block scope (rather than the
// file-scoped form) keeps the output compatible with older C# toolchains.
func csNsOpen(m *model.Module) (string, error) {
ns, err := nsName(m)
if err != nil {
return "", err
}
return fmt.Sprintf("namespace %s\n{", ns), nil
}

// csNsClose closes a block opened with csNsOpen.
func csNsClose(m *model.Module) (string, error) {
ns, err := nsName(m)
if err != nil {
return "", err
}
return fmt.Sprintf("} // namespace %s", ns), nil
}
70 changes: 70 additions & 0 deletions pkg/gen/filters/filtercs/cs_ns_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package filtercs

import (
"testing"

"github.com/apigear-io/cli/pkg/model"

"github.com/stretchr/testify/assert"
)

func TestNS(t *testing.T) {
t.Parallel()
table := []struct {
in string
out string
}{
{"a", "A"},
{"a.b", "A.B"},
{"a.b.c", "A.B.C"},
{"demo.x", "Demo.X"},
}
for _, tt := range table {
t.Run(tt.in, func(t *testing.T) {
m := model.NewModule(tt.in, "1.0")
r, err := csNs(m)
assert.NoError(t, err)
assert.Equal(t, tt.out, r)
})
}
}

func TestNSOpen(t *testing.T) {
t.Parallel()
table := []struct {
in string
out string
}{
{"a", "namespace A\n{"},
{"a.b", "namespace A.B\n{"},
{"a.b.c", "namespace A.B.C\n{"},
}
for _, tt := range table {
t.Run(tt.in, func(t *testing.T) {
m := model.NewModule(tt.in, "1.0")
r, err := csNsOpen(m)
assert.NoError(t, err)
assert.Equal(t, tt.out, r)
})
}
}

func TestNSClose(t *testing.T) {
t.Parallel()
table := []struct {
in string
out string
}{
{"a", "} // namespace A"},
{"a.b", "} // namespace A.B"},
{"a.b.c", "} // namespace A.B.C"},
}
for _, tt := range table {
t.Run(tt.in, func(t *testing.T) {
m := model.NewModule(tt.in, "1.0")
r, err := csNsClose(m)
assert.NoError(t, err)
assert.Equal(t, tt.out, r)
})
}
}
Loading
Loading