Skip to content

Commit a9256fa

Browse files
authored
Merge pull request #1135 from Fellmonkey/sharp-support-type-gen
Adding C# to CLI type generation
2 parents 9b6ef37 + 0681707 commit a9256fa

File tree

3 files changed

+179
-1
lines changed

3 files changed

+179
-1
lines changed

src/SDK/Language/CLI.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,11 @@ public function getFiles(): array
231231
'destination' => 'lib/type-generation/languages/dart.js',
232232
'template' => 'cli/lib/type-generation/languages/dart.js.twig',
233233
],
234+
[
235+
'scope' => 'default',
236+
'destination' => 'lib/type-generation/languages/csharp.js',
237+
'template' => 'cli/lib/type-generation/languages/csharp.js.twig',
238+
],
234239
[
235240
'scope' => 'default',
236241
'destination' => 'lib/questions.js',

templates/cli/lib/commands/types.js.twig

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const { Swift } = require("../type-generation/languages/swift");
1212
const { Java } = require("../type-generation/languages/java");
1313
const { Dart } = require("../type-generation/languages/dart");
1414
const { JavaScript } = require("../type-generation/languages/javascript");
15+
const { CSharp } = require("../type-generation/languages/csharp");
1516

1617
/**
1718
* @param {string} language
@@ -33,6 +34,8 @@ function createLanguageMeta(language) {
3334
return new Java();
3435
case "dart":
3536
return new Dart();
37+
case "cs":
38+
return new CSharp();
3639
default:
3740
throw new Error(`Language '${language}' is not supported`);
3841
}
@@ -55,7 +58,7 @@ const typesLanguageOption = new Option(
5558
"-l, --language <language>",
5659
"The language of the types"
5760
)
58-
.choices(["auto", "ts", "js", "php", "kotlin", "swift", "java", "dart"])
61+
.choices(["auto", "ts", "js", "php", "kotlin", "swift", "java", "dart", "cs"])
5962
.default("auto");
6063

6164
const typesStrictOption = new Option(
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/** @typedef {import('../attribute').Attribute} Attribute */
2+
const { AttributeType } = require('../attribute');
3+
const { LanguageMeta } = require("./language");
4+
5+
class CSharp extends LanguageMeta {
6+
getType(attribute, collections) {
7+
let type = "";
8+
switch (attribute.type) {
9+
case AttributeType.STRING:
10+
case AttributeType.EMAIL:
11+
case AttributeType.DATETIME:
12+
type = "string";
13+
if (attribute.format === AttributeType.ENUM) {
14+
type = LanguageMeta.toPascalCase(attribute.key);
15+
}
16+
break;
17+
case AttributeType.INTEGER:
18+
type = "long";
19+
break;
20+
case AttributeType.FLOAT:
21+
type = "double";
22+
break;
23+
case AttributeType.BOOLEAN:
24+
type = "bool";
25+
break;
26+
case AttributeType.RELATIONSHIP:
27+
const relatedCollection = collections.find(c => c.$id === attribute.relatedCollection);
28+
if (!relatedCollection) {
29+
throw new Error(`Related collection with ID '${attribute.relatedCollection}' not found.`);
30+
}
31+
type = LanguageMeta.toPascalCase(relatedCollection.name);
32+
if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany') {
33+
type = `List<${type}>`;
34+
}
35+
break;
36+
default:
37+
throw new Error(`Unknown attribute type: ${attribute.type}`);
38+
}
39+
if (attribute.array) {
40+
type = `List<${type}>`;
41+
}
42+
if (!attribute.required) {
43+
type += "?";
44+
}
45+
return type;
46+
}
47+
48+
getTemplate() {
49+
return `/// This file is auto-generated by the Appwrite CLI.
50+
/// You can regenerate it by running \`appwrite ${process.argv.slice(2).join(' ')}\`.
51+
52+
#nullable enable
53+
using System;
54+
using System.Collections.Generic;
55+
using System.Linq;
56+
using System.Text.Json.Serialization;
57+
58+
namespace Appwrite.Models
59+
{
60+
<% for (const attribute of collection.attributes) { -%>
61+
<% if (attribute.format === 'enum') { -%>
62+
63+
public enum <%- toPascalCase(attribute.key) %> {
64+
<% for (const [index, element] of Object.entries(attribute.elements) ) { -%>
65+
[JsonPropertyName("<%- element %>")]
66+
<%- toPascalCase(element) %><% if (index < attribute.elements.length - 1) { %>,<% } %>
67+
<% } -%>
68+
}
69+
<% } -%>
70+
<% } %>
71+
public class <%= toPascalCase(collection.name) %>
72+
{
73+
<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%>
74+
[JsonPropertyName("<%- attribute.key %>")]
75+
public <%- getType(attribute, collections) %> <%= toPascalCase(attribute.key) %> { get; private set; }
76+
77+
<% } -%>
78+
79+
public <%= toPascalCase(collection.name) %>(
80+
<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%>
81+
<%- getType(attribute, collections) %> <%= toCamelCase(attribute.key) %><% if (index < collection.attributes.length - 1) { %>,<% } %>
82+
<% } -%>
83+
)
84+
{
85+
<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%>
86+
<%= toPascalCase(attribute.key) %> = <%= toCamelCase(attribute.key) %>;
87+
<% } -%>
88+
}
89+
90+
public static <%= toPascalCase(collection.name) %> From(Dictionary<string, object> map) => new <%= toPascalCase(collection.name) %>(
91+
<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%>
92+
<%- toCamelCase(attribute.key) %>: <%
93+
// ENUM
94+
if (attribute.format === 'enum') {
95+
if (attribute.array) {
96+
-%>((IEnumerable<object>)map["<%- attribute.key %>"]).Select(e => Enum.Parse<Models.<%- toPascalCase(attribute.key) %>>(e.ToString()!, true)).ToList()<%
97+
} else {
98+
-%>Enum.Parse<Models.<%- toPascalCase(attribute.key) %>>(map["<%- attribute.key %>"].ToString()!, true)<%
99+
}
100+
// RELATIONSHIP
101+
} else if (attribute.type === 'relationship') {
102+
const relatedClass = toPascalCase(collections.find(c => c.$id === attribute.relatedCollection).name);
103+
if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany' || attribute.array) {
104+
-%>((IEnumerable<object>)map["<%- attribute.key %>"]).Select(it => Models.<%- relatedClass %>.From((Dictionary<string, object>)it)).ToList()<%
105+
} else {
106+
-%>Models.<%- relatedClass %>.From((Dictionary<string, object>)map["<%- attribute.key %>"])<%
107+
}
108+
// ARRAY TYPES
109+
} else if (attribute.array) {
110+
if (attribute.type === 'string' || attribute.type === 'datetime' || attribute.type === 'email') {
111+
-%>((IEnumerable<object>)map["<%- attribute.key %>"]).Select(x => x?.ToString())<%- attribute.required ? '.Where(x => x != null)' : '' %>.ToList()!<%
112+
} else if (attribute.type === 'integer') {
113+
-%>((IEnumerable<object>)map["<%- attribute.key %>"]).Select(x => <%- !attribute.required ? 'x == null ? (long?)null : ' : '' %>Convert.ToInt64(x)).ToList()<%
114+
} else if (attribute.type === 'double') {
115+
-%>((IEnumerable<object>)map["<%- attribute.key %>"]).Select(x => <%- !attribute.required ? 'x == null ? (double?)null : ' : '' %>Convert.ToDouble(x)).ToList()<%
116+
} else if (attribute.type === 'boolean') {
117+
-%>((IEnumerable<object>)map["<%- attribute.key %>"]).Select(x => <%- !attribute.required ? 'x == null ? (bool?)null : ' : '' %>(bool)x).ToList()<%
118+
}
119+
// SINGLE VALUE TYPES
120+
} else if (attribute.type === 'integer') {
121+
-%><%- !attribute.required ? 'map["' + attribute.key + '"] == null ? null : ' : '' %>Convert.ToInt64(map["<%- attribute.key %>"])<%
122+
} else if (attribute.type === 'double') {
123+
-%><%- !attribute.required ? 'map["' + attribute.key + '"] == null ? null : ' : '' %>Convert.ToDouble(map["<%- attribute.key %>"])<%
124+
} else if (attribute.type === 'boolean') {
125+
-%>(<%- getType(attribute, collections) %>)map["<%- attribute.key %>"]<%
126+
} else if (attribute.type === 'string' || attribute.type === 'datetime' || attribute.type === 'email') {
127+
-%>map["<%- attribute.key %>"]<%- !attribute.required ? '?' : '' %>.ToString()<%- attribute.required ? '!' : ''%><%
128+
} else {
129+
-%>default<%
130+
}
131+
-%><% if (index < collection.attributes.length - 1) { %>,<% } %>
132+
<% } -%>
133+
);
134+
135+
public Dictionary<string, object?> ToMap() => new Dictionary<string, object?>()
136+
{
137+
<% for (const [index, attribute] of Object.entries(collection.attributes)) { -%>
138+
{ "<%- attribute.key %>", <%
139+
// ENUM
140+
if (attribute.format === 'enum') {
141+
if (attribute.array) {
142+
-%><%= toPascalCase(attribute.key) %>?.Select(e => e.ToString()).ToList()<%
143+
} else {
144+
-%><%= toPascalCase(attribute.key) %>?.ToString()<%
145+
}
146+
// RELATIONSHIP
147+
} else if (attribute.type === 'relationship') {
148+
if ((attribute.relationType === 'oneToMany' && attribute.side === 'parent') || (attribute.relationType === 'manyToOne' && attribute.side === 'child') || attribute.relationType === 'manyToMany' || attribute.array) {
149+
-%><%= toPascalCase(attribute.key) %>?.Select(e => e.ToMap()).ToList()<%
150+
} else {
151+
-%><%= toPascalCase(attribute.key) %>?.ToMap()<%
152+
}
153+
// OTHER
154+
} else {
155+
-%><%= toPascalCase(attribute.key) %><%
156+
}
157+
-%> }<% if (index < collection.attributes.length - 1) { %>,<% } %>
158+
<% } -%>
159+
};
160+
}
161+
}
162+
`;
163+
}
164+
165+
getFileName(collection) {
166+
return LanguageMeta.toPascalCase(collection.name) + ".cs";
167+
}
168+
}
169+
170+
module.exports = { CSharp };

0 commit comments

Comments
 (0)