diff --git a/pkg/gen/filters/filtercs/cs_async_return.go b/pkg/gen/filters/filtercs/cs_async_return.go new file mode 100644 index 00000000..6609e185 --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_async_return.go @@ -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. +// 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) +} diff --git a/pkg/gen/filters/filtercs/cs_async_return_test.go b/pkg/gen/filters/filtercs/cs_async_return_test.go new file mode 100644 index 00000000..4f10f7fd --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_async_return_test.go @@ -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"}, + {"test", "Test3", "opInt", "Task"}, + {"test", "Test3", "opInt32", "Task"}, + {"test", "Test3", "opInt64", "Task"}, + {"test", "Test3", "opFloat", "Task"}, + {"test", "Test3", "opFloat64", "Task"}, + {"test", "Test3", "opString", "Task"}, + {"test", "Test3", "opBoolArray", "Task>"}, + {"test", "Test3", "opIntArray", "Task>"}, + {"test", "Test3", "opStringArray", "Task>"}, + } + 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"}, + {"test", "Test4", "opStruct", "Task"}, + {"test", "Test4", "opInterface", "Task"}, + {"test", "Test4", "opEnumArray", "Task>"}, + {"test", "Test4", "opStructArray", "Task>"}, + {"test", "Test4", "opInterfaceArray", "Task>"}, + } + 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) + }) + } + } +} diff --git a/pkg/gen/filters/filtercs/cs_default.go b/pkg/gen/filters/filtercs/cs_default.go new file mode 100644 index 00000000..bd7ca05f --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_default.go @@ -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; strings to string.Empty; byte arrays to Array.Empty(). +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()" + 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) +} diff --git a/pkg/gen/filters/filtercs/cs_default_test.go b/pkg/gen/filters/filtercs/cs_default_test.go new file mode 100644 index 00000000..0324ba24 --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_default_test.go @@ -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()"}, + {"test", "Test1", "propAny", "null"}, + {"test", "Test1", "propBoolArray", "new List()"}, + {"test", "Test1", "propIntArray", "new List()"}, + {"test", "Test1", "propInt64Array", "new List()"}, + {"test", "Test1", "propStringArray", "new List()"}, + } + 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()"}, + {"test", "Test2", "propStructArray", "new List()"}, + {"test", "Test2", "propInterfaceArray", "new List()"}, + } + 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) + }) + } + } +} diff --git a/pkg/gen/filters/filtercs/cs_ns.go b/pkg/gen/filters/filtercs/cs_ns.go new file mode 100644 index 00000000..1032ce70 --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_ns.go @@ -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 +} diff --git a/pkg/gen/filters/filtercs/cs_ns_test.go b/pkg/gen/filters/filtercs/cs_ns_test.go new file mode 100644 index 00000000..3e3ade18 --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_ns_test.go @@ -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) + }) + } +} diff --git a/pkg/gen/filters/filtercs/cs_param.go b/pkg/gen/filters/filtercs/cs_param.go new file mode 100644 index 00000000..899e3601 --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_param.go @@ -0,0 +1,25 @@ +package filtercs + +import ( + "fmt" + + "github.com/apigear-io/cli/pkg/model" +) + +// ToParamString renders a single C# parameter as " ". C# passes +// reference types and List by reference already, so no const/ref qualifier +// is needed (unlike C++). +func ToParamString(prefix string, schema *model.Schema, name string) (string, error) { + ret, err := ToReturnString(prefix, schema) + if err != nil { + return "xxx", fmt.Errorf("csParam type error: %s", err) + } + return fmt.Sprintf("%s %s", ret, name), nil +} + +func csParam(prefix string, node *model.TypedNode) (string, error) { + if node == nil { + return "xxx", fmt.Errorf("csParam node is nil") + } + return ToParamString(prefix, &node.Schema, node.Name) +} diff --git a/pkg/gen/filters/filtercs/cs_param_test.go b/pkg/gen/filters/filtercs/cs_param_test.go new file mode 100644 index 00000000..bf5111bb --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_param_test.go @@ -0,0 +1,114 @@ +package filtercs + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParam(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + for _, sys := range syss { + op := sys.LookupOperation("test", "Test3", "opString") + assert.NotNil(t, op) + r, err := csParam("", op.Params[0]) + assert.NoError(t, err) + assert.Equal(t, "string param1", r) + } +} + +func TestParams(t *testing.T) { + t.Parallel() + table := []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test3", "opBool", "bool param1"}, + {"test", "Test3", "opInt", "int param1"}, + {"test", "Test3", "opInt32", "int param1"}, + {"test", "Test3", "opInt64", "long param1"}, + {"test", "Test3", "opFloat", "float param1"}, + {"test", "Test3", "opFloat32", "float param1"}, + {"test", "Test3", "opFloat64", "double param1"}, + {"test", "Test3", "opString", "string param1"}, + {"test", "Test3", "opBoolArray", "List param1"}, + {"test", "Test3", "opIntArray", "List param1"}, + {"test", "Test3", "opStringArray", "List param1"}, + {"test", "Test3", "op_Bool", "bool param_Bool"}, + {"test", "Test3", "op_bool", "bool param_bool"}, + {"test", "Test3", "op_1", "bool param_1"}, + } + syss := loadTestSystems(t) + for _, sys := range syss { + for _, tt := range table { + t.Run(tt.pn, func(t *testing.T) { + meth := sys.LookupOperation(tt.mn, tt.in, tt.pn) + assert.NotNil(t, meth) + r, err := csParams("", meth.Params) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestParamsSymbols(t *testing.T) { + t.Parallel() + table := []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test4", "opEnum", "Enum1 param1"}, + {"test", "Test4", "opStruct", "Struct1 param1"}, + {"test", "Test4", "opInterface", "IInterface1 param1"}, + {"test", "Test4", "opEnumArray", "List param1"}, + {"test", "Test4", "opStructArray", "List param1"}, + {"test", "Test4", "opInterfaceArray", "List param1"}, + } + syss := loadTestSystems(t) + for _, sys := range syss { + for _, tt := range table { + t.Run(tt.pn, func(t *testing.T) { + op := sys.LookupOperation(tt.mn, tt.in, tt.pn) + assert.NotNil(t, op) + r, err := csParams("", op.Params) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestParamsMultiple(t *testing.T) { + t.Parallel() + table := []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test5", "opBoolBool", "bool param1, bool param2"}, + {"test", "Test5", "opIntInt", "int param1, int param2"}, + {"test", "Test5", "opStringString", "string param1, string param2"}, + {"test", "Test5", "opEnumEnum", "Enum1 param1, Enum1 param2"}, + {"test", "Test5", "opStructStruct", "Struct1 param1, Struct1 param2"}, + {"test", "Test5", "opInterfaceInterface", "IInterface1 param1, IInterface1 param2"}, + } + syss := loadTestSystems(t) + for _, sys := range syss { + for _, tt := range table { + t.Run(tt.pn, func(t *testing.T) { + op := sys.LookupOperation(tt.mn, tt.in, tt.pn) + assert.NotNil(t, op) + r, err := csParams("", op.Params) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} diff --git a/pkg/gen/filters/filtercs/cs_params.go b/pkg/gen/filters/filtercs/cs_params.go new file mode 100644 index 00000000..4e4602e6 --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_params.go @@ -0,0 +1,23 @@ +package filtercs + +import ( + "fmt" + "strings" + + "github.com/apigear-io/cli/pkg/model" +) + +func csParams(prefix string, nodes []*model.TypedNode) (string, error) { + if nodes == nil { + return "xxx", fmt.Errorf("csParams called with nil nodes") + } + var params []string + for _, p := range nodes { + r, err := ToParamString(prefix, &p.Schema, p.Name) + if err != nil { + return "xxx", err + } + params = append(params, r) + } + return strings.Join(params, ", "), nil +} diff --git a/pkg/gen/filters/filtercs/cs_return.go b/pkg/gen/filters/filtercs/cs_return.go new file mode 100644 index 00000000..72baf5f2 --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_return.go @@ -0,0 +1,84 @@ +package filtercs + +import ( + "fmt" + + "github.com/apigear-io/cli/pkg/gen/filters/common" + "github.com/apigear-io/cli/pkg/model" +) + +// ToReturnString deducts the C# type for a schema. Arrays map to the +// generic System.Collections.Generic.List. +func ToReturnString(prefix string, schema *model.Schema) (string, error) { + if schema == nil { + return "xxx", fmt.Errorf("ToReturnString schema is nil") + } + text := "" + switch schema.KindType { + case model.TypeVoid: + text = "void" + case model.TypeBool: + text = "bool" + case model.TypeInt, model.TypeInt32: + text = "int" + case model.TypeInt64: + text = "long" + case model.TypeFloat, model.TypeFloat32: + text = "float" + case model.TypeFloat64: + text = "double" + case model.TypeString: + text = "string" + case model.TypeBytes: + text = "byte[]" + case model.TypeAny: + text = "object" + case model.TypeExtern: + xe := parseCsExtern(schema) + if xe.Namespace != "" { + prefix = fmt.Sprintf("%s.", xe.Namespace) + } else { + // Externs carry their own fully-qualified name; do not prefix them. + prefix = "" + } + text = fmt.Sprintf("%s%s", prefix, xe.Name) + case model.TypeEnum: + e := schema.LookupEnum(schema.Import, schema.Type) + if schema.Import != "" { + prefix = fmt.Sprintf("%s.%s.", common.CamelTitleCase(schema.System().Name), common.CamelTitleCase(schema.Import)) + } + if e != nil { + text = fmt.Sprintf("%s%s", prefix, common.CamelTitleCase(e.Name)) + } + case model.TypeStruct: + s := schema.LookupStruct(schema.Import, schema.Type) + if schema.Import != "" { + prefix = fmt.Sprintf("%s.%s.", common.CamelTitleCase(schema.System().Name), common.CamelTitleCase(schema.Import)) + } + if s != nil { + text = fmt.Sprintf("%s%s", prefix, common.CamelTitleCase(s.Name)) + } + case model.TypeInterface: + i := schema.LookupInterface(schema.Import, schema.Type) + if schema.Import != "" { + prefix = fmt.Sprintf("%s.%s.", common.CamelTitleCase(schema.System().Name), common.CamelTitleCase(schema.Import)) + } + if i != nil { + text = fmt.Sprintf("%sI%s", prefix, common.CamelTitleCase(i.Name)) + } + default: + return "xxx", fmt.Errorf("csReturn unknown schema %s", schema.Dump()) + } + if schema.IsArray { + return fmt.Sprintf("List<%s>", text), nil + } + return text, nil +} + +// csReturn casts the value to a TypedNode and deducts the C# return type. +func csReturn(prefix string, node *model.TypedNode) (string, error) { + if node == nil { + return "xxx", fmt.Errorf("csReturn node is nil") + } + return ToReturnString(prefix, &node.Schema) +} diff --git a/pkg/gen/filters/filtercs/cs_return_test.go b/pkg/gen/filters/filtercs/cs_return_test.go new file mode 100644 index 00000000..05b9973c --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_return_test.go @@ -0,0 +1,159 @@ +package filtercs + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +// test with all the primitive and array types +func TestReturn(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test1", "propVoid", "void"}, + {"test", "Test1", "propBool", "bool"}, + {"test", "Test1", "propInt", "int"}, + {"test", "Test1", "propInt32", "int"}, + {"test", "Test1", "propInt64", "long"}, + {"test", "Test1", "propFloat", "float"}, + {"test", "Test1", "propFloat32", "float"}, + {"test", "Test1", "propFloat64", "double"}, + {"test", "Test1", "propString", "string"}, + {"test", "Test1", "propBytes", "byte[]"}, + {"test", "Test1", "propAny", "object"}, + {"test", "Test1", "propBoolArray", "List"}, + {"test", "Test1", "propIntArray", "List"}, + {"test", "Test1", "propInt32Array", "List"}, + {"test", "Test1", "propInt64Array", "List"}, + {"test", "Test1", "propFloatArray", "List"}, + {"test", "Test1", "propFloat32Array", "List"}, + {"test", "Test1", "propFloat64Array", "List"}, + {"test", "Test1", "propStringArray", "List"}, + } + 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 := csReturn("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestOperationReturn(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test3", "opVoid", "void"}, + {"test", "Test3", "opBool", "bool"}, + {"test", "Test3", "opInt", "int"}, + {"test", "Test3", "opInt32", "int"}, + {"test", "Test3", "opInt64", "long"}, + {"test", "Test3", "opFloat", "float"}, + {"test", "Test3", "opFloat32", "float"}, + {"test", "Test3", "opFloat64", "double"}, + {"test", "Test3", "opString", "string"}, + {"test", "Test3", "opBoolArray", "List"}, + {"test", "Test3", "opIntArray", "List"}, + {"test", "Test3", "opFloatArray", "List"}, + {"test", "Test3", "opStringArray", "List"}, + } + 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 := csReturn("", op.Return) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestReturnSymbols(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test2", "propEnum", "Enum1"}, + {"test", "Test2", "propStruct", "Struct1"}, + {"test", "Test2", "propInterface", "IInterface1"}, + {"test", "Test2", "propEnumArray", "List"}, + {"test", "Test2", "propStructArray", "List"}, + {"test", "Test2", "propInterfaceArray", "List"}, + } + 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 := csReturn("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestPrefixedReturn(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test1", "propInt", "int"}, + {"test", "Test1", "propString", "string"}, + {"test", "Test1", "propIntArray", "List"}, + {"test", "Test2", "propEnum", "MyPrefix.Enum1"}, + {"test", "Test2", "propStruct", "MyPrefix.Struct1"}, + {"test", "Test2", "propInterface", "MyPrefix.IInterface1"}, + {"test", "Test2", "propEnumArray", "List"}, + {"test", "Test2", "propStructArray", "List"}, + {"test", "Test2", "propInterfaceArray", "List"}, + } + 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 := csReturn("MyPrefix.", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestType(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + for _, sys := range syss { + prop := sys.LookupProperty("test", "Test2", "propStruct") + assert.NotNil(t, prop) + r, err := csType("", prop) + assert.NoError(t, err) + assert.Equal(t, "Struct1", r) + } +} diff --git a/pkg/gen/filters/filtercs/cs_testvalue.go b/pkg/gen/filters/filtercs/cs_testvalue.go new file mode 100644 index 00000000..2723986f --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_testvalue.go @@ -0,0 +1,90 @@ +package filtercs + +import ( + "fmt" + + "github.com/apigear-io/cli/pkg/gen/filters/common" + "github.com/apigear-io/cli/pkg/model" +) + +// ToTestValueString returns a non-default test value for a given schema. +// We intentionally ignore arrays in order to return the test value of the +// inner type (it can be placed into a collection by the template). +func ToTestValueString(prefix string, schema *model.Schema) (string, error) { + if schema == nil { + return "xxx", fmt.Errorf("csTestValue schema is nil") + } + var text string + switch schema.KindType { + case model.TypeString: + text = "\"xyz\"" + case model.TypeInt, model.TypeInt32: + text = "1" + case model.TypeInt64: + text = "1L" + case model.TypeFloat, model.TypeFloat32: + text = "1.0f" + case model.TypeFloat64: + text = "1.0" + case model.TypeBool: + text = "true" + case model.TypeBytes: + text = "new byte[] { 1 }" + case model.TypeAny: + text = "1" + case model.TypeVoid: + text = "" + 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("csTestValue enum not found: %s", schema.Dump()) + } + member := e.Members[0].Name + if len(e.Members) > 1 { + member = e.Members[1].Name + } + 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(member)) + case model.TypeStruct: + s := schema.LookupStruct(schema.Import, schema.Type) + if s == nil { + return "xxx", fmt.Errorf("csTestValue 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: + i := schema.LookupInterface(schema.Import, schema.Type) + if i == nil { + return "xxx", fmt.Errorf("csTestValue interface 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(i.Name)) + default: + return "xxx", fmt.Errorf("csTestValue unknown schema %s", schema.Dump()) + } + return text, nil +} + +func csTestValue(prefix string, node *model.TypedNode) (string, error) { + if node == nil { + return "xxx", fmt.Errorf("csTestValue node is nil") + } + return ToTestValueString(prefix, &node.Schema) +} diff --git a/pkg/gen/filters/filtercs/cs_testvalue_test.go b/pkg/gen/filters/filtercs/cs_testvalue_test.go new file mode 100644 index 00000000..f3370a9e --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_testvalue_test.go @@ -0,0 +1,108 @@ +package filtercs + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTestValue(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test1", "propVoid", ""}, + {"test", "Test1", "propBool", "true"}, + {"test", "Test1", "propInt", "1"}, + {"test", "Test1", "propInt32", "1"}, + {"test", "Test1", "propInt64", "1L"}, + {"test", "Test1", "propFloat", "1.0f"}, + {"test", "Test1", "propFloat32", "1.0f"}, + {"test", "Test1", "propFloat64", "1.0"}, + {"test", "Test1", "propString", "\"xyz\""}, + {"test", "Test1", "propBytes", "new byte[] { 1 }"}, + {"test", "Test1", "propAny", "1"}, + // array types intentionally return the inner element value + {"test", "Test1", "propBoolArray", "true"}, + {"test", "Test1", "propIntArray", "1"}, + {"test", "Test1", "propStringArray", "\"xyz\""}, + } + 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 := csTestValue("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestTestValueSymbols(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test2", "propEnum", "Enum1.NotDefault"}, + {"test", "InterfaceNamesCheck", "lowerEnumProp", "EnumLowerNames.SecondValue"}, + {"test", "Test2", "propStruct", "new Struct1()"}, + {"test", "Test2", "propInterface", "new Interface1()"}, + {"test", "Test2", "propEnumArray", "Enum1.NotDefault"}, + {"test", "Test2", "propStructArray", "new Struct1()"}, + {"test", "Test2", "propInterfaceArray", "new Interface1()"}, + } + 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 := csTestValue("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestTestValueSymbolsWithPrefix(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + var propTests = []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test2", "propEnum", "MyPrefix.Enum1.NotDefault"}, + {"test", "Test2", "propStruct", "new MyPrefix.Struct1()"}, + {"test", "Test2", "propInterface", "new MyPrefix.Interface1()"}, + } + 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 := csTestValue("MyPrefix.", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestTestValueWithErrors(t *testing.T) { + t.Parallel() + s, err := csTestValue("", nil) + assert.Error(t, err) + assert.Equal(t, "xxx", s) +} diff --git a/pkg/gen/filters/filtercs/cs_type.go b/pkg/gen/filters/filtercs/cs_type.go new file mode 100644 index 00000000..c522a0ba --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_type.go @@ -0,0 +1,4 @@ +package filtercs + +// csType is an alias for csReturn: the plain C# type of a node. +var csType = csReturn diff --git a/pkg/gen/filters/filtercs/cs_var.go b/pkg/gen/filters/filtercs/cs_var.go new file mode 100644 index 00000000..2c517f8b --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_var.go @@ -0,0 +1,18 @@ +package filtercs + +import ( + "fmt" + + "github.com/apigear-io/cli/pkg/model" +) + +func ToVarString(node *model.TypedNode) (string, error) { + if node == nil { + return "xxx", fmt.Errorf("ToVarString node is nil") + } + return node.Name, nil +} + +func csVar(node *model.TypedNode) (string, error) { + return ToVarString(node) +} diff --git a/pkg/gen/filters/filtercs/cs_var_test.go b/pkg/gen/filters/filtercs/cs_var_test.go new file mode 100644 index 00000000..495385f2 --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_var_test.go @@ -0,0 +1,45 @@ +package filtercs + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestVar(t *testing.T) { + t.Parallel() + syss := loadTestSystems(t) + for _, sys := range syss { + op := sys.LookupOperation("test", "Test3", "opString") + assert.NotNil(t, op) + r, err := csVar(op.Params[0]) + assert.NoError(t, err) + assert.Equal(t, "param1", r) + } +} + +func TestVars(t *testing.T) { + t.Parallel() + table := []struct { + mn string + in string + pn string + rt string + }{ + {"test", "Test5", "opBoolBool", "param1, param2"}, + {"test", "Test5", "opStringString", "param1, param2"}, + {"test", "Test3", "op_Bool", "param_Bool"}, + } + syss := loadTestSystems(t) + for _, sys := range syss { + for _, tt := range table { + t.Run(tt.pn, func(t *testing.T) { + op := sys.LookupOperation(tt.mn, tt.in, tt.pn) + assert.NotNil(t, op) + r, err := csVars(op.Params) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} diff --git a/pkg/gen/filters/filtercs/cs_vars.go b/pkg/gen/filters/filtercs/cs_vars.go new file mode 100644 index 00000000..4132c7de --- /dev/null +++ b/pkg/gen/filters/filtercs/cs_vars.go @@ -0,0 +1,23 @@ +package filtercs + +import ( + "fmt" + "strings" + + "github.com/apigear-io/cli/pkg/model" +) + +func csVars(nodes []*model.TypedNode) (string, error) { + if nodes == nil { + return "xxx", fmt.Errorf("csVars called with nil nodes") + } + names := make([]string, len(nodes)) + for idx, p := range nodes { + name, err := ToVarString(p) + if err != nil { + return "xxx", err + } + names[idx] = name + } + return strings.Join(names, ", "), nil +} diff --git a/pkg/gen/filters/filtercs/extern.go b/pkg/gen/filters/filtercs/extern.go new file mode 100644 index 00000000..b1e78c34 --- /dev/null +++ b/pkg/gen/filters/filtercs/extern.go @@ -0,0 +1,52 @@ +package filtercs + +import "github.com/apigear-io/cli/pkg/model" + +// CsExtern holds the C# binding information for an external type, read from +// the extern's "cs.*" meta keys. +type CsExtern struct { + Namespace string + Name string + Default string + Using string + Package string + Version string +} + +func parseCsExtern(schema *model.Schema) CsExtern { + xe := schema.GetExtern() + return csExtern(xe) +} + +// MakeCsExtern exposes the parsed extern info to templates via a schema. +func MakeCsExtern(schema *model.Schema) CsExtern { + return parseCsExtern(schema) +} + +func csExtern(xe *model.Extern) CsExtern { + ns := xe.Meta.GetString("cs.namespace") + name := xe.Meta.GetString("cs.name") + dft := xe.Meta.GetString("cs.default") + using := xe.Meta.GetString("cs.using") + pkg := xe.Meta.GetString("cs.package") + ver := xe.Meta.GetString("cs.version") + if name == "" { + name = xe.Name + } + return CsExtern{ + Namespace: ns, + Name: name, + Default: dft, + Using: using, + Package: pkg, + Version: ver, + } +} + +func csExterns(externs []*model.Extern) []CsExtern { + items := []CsExtern{} + for _, ex := range externs { + items = append(items, csExtern(ex)) + } + return items +} diff --git a/pkg/gen/filters/filtercs/extern_test.go b/pkg/gen/filters/filtercs/extern_test.go new file mode 100644 index 00000000..51fcc3c2 --- /dev/null +++ b/pkg/gen/filters/filtercs/extern_test.go @@ -0,0 +1,126 @@ +package filtercs + +import ( + "testing" + + "github.com/apigear-io/cli/pkg/model" + "github.com/stretchr/testify/assert" +) + +func makeExtern(name string, meta model.Meta) *model.Extern { + return &model.Extern{NamedNode: model.NamedNode{Name: name, Meta: meta}} +} + +func TestExternParsing(t *testing.T) { + t.Parallel() + xe := makeExtern("Foo", model.Meta{ + "cs.namespace": "Bar.Baz", + "cs.name": "FooX", + "cs.default": "MakeFoo()", + "cs.using": "Bar.Baz", + "cs.package": "Bar.Pkg", + "cs.version": "1.2.3", + }) + got := csExtern(xe) + assert.Equal(t, "Bar.Baz", got.Namespace) + assert.Equal(t, "FooX", got.Name) + assert.Equal(t, "MakeFoo()", got.Default) + assert.Equal(t, "Bar.Baz", got.Using) + assert.Equal(t, "Bar.Pkg", got.Package) + assert.Equal(t, "1.2.3", got.Version) +} + +func TestExternNameFallsBackToExternName(t *testing.T) { + t.Parallel() + got := csExtern(makeExtern("Plain", model.Meta{})) + assert.Equal(t, "Plain", got.Name) + assert.Equal(t, "", got.Namespace) +} + +func TestExterns(t *testing.T) { + t.Parallel() + externs := []*model.Extern{ + makeExtern("A", model.Meta{"cs.namespace": "N1"}), + makeExtern("B", model.Meta{"cs.name": "BB"}), + } + got := csExterns(externs) + assert.Len(t, got, 2) + assert.Equal(t, "N1", got[0].Namespace) + assert.Equal(t, "A", got[0].Name) + assert.Equal(t, "BB", got[1].Name) +} + +func TestExternReturn(t *testing.T) { + syss := loadExternSystems(t) + table := []struct { + mn string + in string + pn string + rt string + }{ + {"demo", "Iface1", "prop1", "XType1"}, + {"demo", "Iface1", "prop2", "Demo.X.XType2"}, + {"demo", "Iface1", "prop3", "Demo.X.XType3A"}, + } + for _, sys := range syss { + for _, tt := range table { + t.Run(tt.pn, func(t *testing.T) { + prop := sys.LookupProperty(tt.mn, tt.in, tt.pn) + assert.NotNil(t, prop) + r, err := csReturn("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} + +func TestExternDefault(t *testing.T) { + syss := loadExternSystems(t) + table := []struct { + mn string + in string + pn string + rt string + }{ + {"demo", "Iface1", "prop1", "new XType1()"}, + {"demo", "Iface1", "prop2", "new Demo.X.XType2()"}, + {"demo", "Iface1", "prop3", "Demo.X.XTypeFactory.Create()"}, + } + for _, sys := range syss { + for _, tt := range table { + 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 TestExternTestValue(t *testing.T) { + syss := loadExternSystems(t) + table := []struct { + mn string + in string + pn string + rt string + }{ + {"demo", "Iface1", "prop1", "new XType1()"}, + {"demo", "Iface1", "prop2", "new Demo.X.XType2()"}, + {"demo", "Iface1", "prop3", "Demo.X.XTypeFactory.Create()"}, + } + for _, sys := range syss { + for _, tt := range table { + t.Run(tt.pn, func(t *testing.T) { + prop := sys.LookupProperty(tt.mn, tt.in, tt.pn) + assert.NotNil(t, prop) + r, err := csTestValue("", prop) + assert.NoError(t, err) + assert.Equal(t, tt.rt, r) + }) + } + } +} diff --git a/pkg/gen/filters/filtercs/filters.go b/pkg/gen/filters/filtercs/filters.go new file mode 100644 index 00000000..127f61c3 --- /dev/null +++ b/pkg/gen/filters/filtercs/filters.go @@ -0,0 +1,21 @@ +package filtercs + +import "text/template" + +// PopulateFuncMap fills the given FuncMap with the C# filter functions. +func PopulateFuncMap(fm template.FuncMap) { + fm["csReturn"] = csReturn + fm["csType"] = csType + fm["csDefault"] = csDefault + fm["csParam"] = csParam + fm["csParams"] = csParams + fm["csVar"] = csVar + fm["csVars"] = csVars + fm["csTestValue"] = csTestValue + fm["csAsyncReturn"] = csAsyncReturn + fm["csNs"] = csNs + fm["csNsOpen"] = csNsOpen + fm["csNsClose"] = csNsClose + fm["csExtern"] = csExtern + fm["csExterns"] = csExterns +} diff --git a/pkg/gen/filters/filtercs/filters_test.go b/pkg/gen/filters/filtercs/filters_test.go new file mode 100644 index 00000000..96b457a4 --- /dev/null +++ b/pkg/gen/filters/filtercs/filters_test.go @@ -0,0 +1,34 @@ +package filtercs + +import ( + "testing" + "text/template" + + "github.com/stretchr/testify/assert" +) + +func TestPopulateFuncMap(t *testing.T) { + t.Parallel() + fm := template.FuncMap{} + PopulateFuncMap(fm) + expected := []string{ + "csReturn", + "csType", + "csDefault", + "csParam", + "csParams", + "csVar", + "csVars", + "csTestValue", + "csAsyncReturn", + "csNs", + "csNsOpen", + "csNsClose", + "csExtern", + "csExterns", + } + for _, name := range expected { + _, ok := fm[name] + assert.Truef(t, ok, "expected func %q to be registered", name) + } +} diff --git a/pkg/gen/filters/filtercs/loader.go b/pkg/gen/filters/filtercs/loader.go new file mode 100644 index 00000000..1d0d9ebe --- /dev/null +++ b/pkg/gen/filters/filtercs/loader.go @@ -0,0 +1,58 @@ +package filtercs + +import ( + "testing" + + "github.com/apigear-io/cli/pkg/idl" + "github.com/apigear-io/cli/pkg/model" + "github.com/stretchr/testify/assert" +) + +func loadTestSystems(t *testing.T) []*model.System { + t.Helper() + sys1 := model.NewSystem("sys1") + p := idl.NewParser(sys1) + err := p.ParseFile("../testdata/test.idl") + assert.NoError(t, err) + err = sys1.Validate() + assert.NoError(t, err) + + sys2 := model.NewSystem("sys2") + dp := model.NewDataParser(sys2) + err = dp.ParseFile("../testdata/test.module.yaml") + assert.NoError(t, err) + err = sys2.Validate() + assert.NoError(t, err) + return []*model.System{sys1} +} + +func loadExternSystems(t *testing.T) []*model.System { + t.Helper() + sys1 := model.NewSystem("sys1") + p := idl.NewParser(sys1) + err := p.ParseFile("../testdata/extern.idl") + assert.NoError(t, err) + + err = p.ParseFile("../testdata/extern2.idl") + assert.NoError(t, err) + err = sys1.Validate() + assert.NoError(t, err) + + parser := model.NewDataParser(sys1) + err = parser.ParseFile("../testdata/test.module.yaml") + assert.NoError(t, err) + err = sys1.Validate() + assert.NoError(t, err) + + err = parser.ParseFile("../testdata/extern_types.module.yaml") + assert.NoError(t, err) + err = sys1.Validate() + assert.NoError(t, err) + + err = parser.ParseFile("../testdata/test_apigear_next.module.yaml") + assert.NoError(t, err) + err = sys1.Validate() + assert.NoError(t, err) + + return []*model.System{sys1} +} diff --git a/pkg/gen/filters/funcmap.go b/pkg/gen/filters/funcmap.go index 16af556a..5db39293 100644 --- a/pkg/gen/filters/funcmap.go +++ b/pkg/gen/filters/funcmap.go @@ -5,6 +5,7 @@ import ( "github.com/apigear-io/cli/pkg/gen/filters/common" "github.com/apigear-io/cli/pkg/gen/filters/filtercpp" + "github.com/apigear-io/cli/pkg/gen/filters/filtercs" "github.com/apigear-io/cli/pkg/gen/filters/filtergo" "github.com/apigear-io/cli/pkg/gen/filters/filterjava" "github.com/apigear-io/cli/pkg/gen/filters/filterjni" @@ -21,6 +22,7 @@ func PopulateFuncMap() template.FuncMap { common.PopulateFuncMap(fm) filtercpp.PopulateFuncMap(fm) + filtercs.PopulateFuncMap(fm) filtergo.PopulateFuncMap(fm) filterts.PopulateFuncMap(fm) filterpy.PopulateFuncMap(fm) diff --git a/pkg/gen/filters/testdata/extern.idl b/pkg/gen/filters/testdata/extern.idl index abc67f49..e8b96154 100644 --- a/pkg/gen/filters/testdata/extern.idl +++ b/pkg/gen/filters/testdata/extern.idl @@ -10,6 +10,7 @@ extern XType1 @cpp.namespace: "demo::x" @cpp.include: "x.h" @java.package: "demo.x" +@cs.namespace: "Demo.X" extern XType2 // external type imported from another module with alias @@ -26,6 +27,10 @@ extern XType2 @java.package: "demo.x" @java.name: "XType3A" + +@cs.namespace: "Demo.X" +@cs.name: "XType3A" +@cs.default: "Demo.X.XTypeFactory.Create()" extern XType3 diff --git a/pkg/gen/filters/testdata/extern2.idl b/pkg/gen/filters/testdata/extern2.idl index f56f0dec..867fbec5 100644 --- a/pkg/gen/filters/testdata/extern2.idl +++ b/pkg/gen/filters/testdata/extern2.idl @@ -10,6 +10,8 @@ extern XType1 @cpp.include: "x.h" @py.module: "demo.x" @java.package: "demo.x" +@cs.name: "XType2" +@cs.namespace: "Demo.X" extern XType2 // external type imported from another module with alias @@ -23,4 +25,7 @@ extern XType2 @py.name: "XType3A" @java.package: "demo.x" @java.name: "XType3A" +@cs.name: "XType3A" +@cs.namespace: "Demo.X" +@cs.default: "Demo.X.XTypeFactory.Create()" extern XType3 \ No newline at end of file diff --git a/pkg/gen/filters/testdata/extern_types.module.yaml b/pkg/gen/filters/testdata/extern_types.module.yaml index 6a659788..c7c7ea61 100644 --- a/pkg/gen/filters/testdata/extern_types.module.yaml +++ b/pkg/gen/filters/testdata/extern_types.module.yaml @@ -18,6 +18,8 @@ externs: py.import: "demo.x" java.package: "demo.x" java.name: "XType2A" + cs.namespace: "Demo.X" + cs.name: "XType2A" - name: XType3 meta: qt.type: XType3A @@ -37,4 +39,7 @@ externs: py.default: "demo.x.createXType3A()" java.package: "demo.x" java.name: "XType3A" - java.default: "someCtorXType3A()" \ No newline at end of file + java.default: "someCtorXType3A()" + cs.namespace: "Demo.X" + cs.name: "XType3A" + cs.default: "Demo.X.XTypeFactory.Create()" \ No newline at end of file