Skip to content

Commit

Permalink
changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jonaslagoni committed Oct 29, 2024
1 parent f4976c9 commit a22a35a
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 66 deletions.
2 changes: 2 additions & 0 deletions docs/presets.md
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,8 @@ This preset is a generator for the meta model `ConstrainedEnumModel` and [can be
| Method | Description | Additional arguments |
|---|---|---|
| `item` | A method to extend enum's item. | `item` object as a [`ConstrainedEnumValueModel`](./internal-model.md#the-constrained-meta-model) instance, which contains the value and key of enum's item. |
| `extension` | A method to extend the enums extension class. | |
| `extensionMethods` | A method to extend the enums extension class methods instead of the whole class. | |

### Rust
#### **Struct**
Expand Down
15 changes: 11 additions & 4 deletions src/generators/csharp/CSharpPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
PresetArgs,
PropertyArgs,
ConstrainedObjectModel,
InterfacePreset
InterfacePreset,
ConstrainedEnumModel
} from '../../models';
import { CSharpOptions } from './CSharpGenerator';
import {
Expand Down Expand Up @@ -40,12 +41,18 @@ export interface CsharpRecordPreset<O>

export type ClassPresetType<O> = CsharpClassPreset<O>;
export type RecordPresetType<O> = CsharpRecordPreset<O>;
export type EnumPresetType<O> = EnumPreset<EnumRenderer, O>;

export interface EnumPresetType<O> extends EnumPreset<EnumRenderer, O> {
extension?: (
args: PresetArgs<EnumRenderer, O, ConstrainedEnumModel>
) => Promise<string> | string;
extensionMethods?: (
args: PresetArgs<EnumRenderer, O, ConstrainedEnumModel>
) => Promise<string> | string;
};
export type CSharpPreset<O = any> = Preset<{
class: CsharpClassPreset<O>;
record: CsharpRecordPreset<O>;
enum: EnumPreset<EnumRenderer, O>;
enum: EnumPresetType<O>;
}>;

export const CSHARP_DEFAULT_PRESET: CSharpPreset<CSharpOptions> = {
Expand Down
5 changes: 5 additions & 0 deletions src/generators/csharp/constrainer/EnumConstrainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ export function defaultEnumKeyConstraints(
export function defaultEnumValueConstraints(): CSharpEnumValueConstraint {
return ({ enumValue }) => {
let normalizedEnumValue;
if(enumValue === null) return enumValue;
if(Array.isArray(enumValue)) return `"${JSON.stringify(enumValue).replace(
/"/g,
'\\"'
)}"`;
switch (typeof enumValue) {
case 'boolean':
case 'bigint':
Expand Down
140 changes: 112 additions & 28 deletions src/generators/csharp/presets/JsonSerializerPreset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ function renderSerializeProperty(
modelInstanceVariable: string,
model: ConstrainedObjectPropertyModel
) {
let value = modelInstanceVariable;
//Special case where a referenced enum model need to be accessed
if (
model.property instanceof ConstrainedReferenceModel &&
model.property.ref instanceof ConstrainedEnumModel
) {
value = `${modelInstanceVariable}${model.required ? '' : '?'}.GetValue()`;
return `writer.WriteRawValue(${modelInstanceVariable}${model.required ? '' : '?'}.GetRawJsonValue(), true);`;
}
return `JsonSerializer.Serialize(writer, ${value}, options);`;
return `JsonSerializer.Serialize(writer, ${modelInstanceVariable}, options);`;
}

function renderSerializeProperties(model: ConstrainedObjectModel) {
Expand All @@ -31,7 +30,7 @@ function renderSerializeProperties(model: ConstrainedObjectModel) {
for (const [propertyName, propertyModel] of Object.entries(
model.properties
)) {
const modelInstanceVariable = `value.${pascalCase(propertyName)}`;
const modelInstanceVariable = `value?.${pascalCase(propertyName)}`;
if (
propertyModel.property instanceof ConstrainedDictionaryModel &&
propertyModel.property.serializationType === 'unwrap'
Expand Down Expand Up @@ -74,13 +73,13 @@ function renderPropertiesList(
);
})
.map((value) => {
return `prop.Name != "${pascalCase(value.propertyName)}"`;
return `prop.Name != "${pascalCase(value?.propertyName)}"`;
});

let propertiesList = 'var properties = value.GetType().GetProperties();';
let propertiesList = 'var properties = value?.GetType().GetProperties();';
if (unwrappedDictionaryProperties.length > 0) {
renderer.dependencyManager.addDependency('using System.Linq;');
propertiesList = `var properties = value.GetType().GetProperties().Where(prop => ${unwrappedDictionaryProperties.join(
propertiesList = `var properties = value?.GetType().GetProperties().Where(prop => ${unwrappedDictionaryProperties.join(
' && '
)});`;
}
Expand All @@ -103,11 +102,6 @@ function renderSerialize({
model.name
} value, JsonSerializerOptions options)
{
if (value == null)
{
JsonSerializer.Serialize(writer, null, options);
return;
}
${propertiesList}
writer.WriteStartObject();
Expand All @@ -124,9 +118,15 @@ function renderDeserializeProperty(model: ConstrainedObjectPropertyModel) {
model.property instanceof ConstrainedReferenceModel &&
model.property.ref instanceof ConstrainedEnumModel
) {
return `${model.property.name}Extensions.To${model.property.name}(JsonSerializer.Deserialize<dynamic>(ref reader, options))`;
return `${model.property.name}Extensions.FromJsonTo${model.property.name}(JsonSerializer.Deserialize<dynamic>(ref reader))`;
} else if (
model.property instanceof ConstrainedReferenceModel &&
model.property.ref instanceof ConstrainedObjectModel
) {
return `JsonSerializer.Deserialize<${model.property.name}>(ref reader)`;
}
return `JsonSerializer.Deserialize<${model.property.type}>(ref reader, options)`;

return `JsonSerializer.Deserialize<${model.property.type}>(ref reader)`;
}

function renderDeserializeProperties(model: ConstrainedObjectModel) {
Expand All @@ -141,7 +141,7 @@ function renderDeserializeProperties(model: ConstrainedObjectModel) {
) {
return `if(instance.${pascalProp} == null) { instance.${pascalProp} = new Dictionary<${
propModel.property.key.type
}, ${propModel.property.value.type}>(); }
}, ${propModel.property.value?.type}>(); }
var deserializedValue = ${renderDeserializeProperty(propModel)};
instance.${pascalProp}.Add(propertyName, deserializedValue);
continue;`;
Expand All @@ -151,8 +151,7 @@ function renderDeserializeProperties(model: ConstrainedObjectModel) {
}
return `if (propertyName == "${propModel.unconstrainedPropertyName}")
{
var value = ${renderDeserializeProperty(propModel)};
instance.${pascalProp} = value;
instance.${pascalProp} = ${renderDeserializeProperty(propModel)};
continue;
}`;
})
Expand Down Expand Up @@ -196,6 +195,9 @@ function renderDeserialize({
}
string propertyName = reader.GetString();
// Advance to the value token
reader.Read();
${renderer.indent(deserializeProperties, 4)}
}
Expand All @@ -219,11 +221,9 @@ public string Serialize(JsonSerializerOptions options = null)
{
return JsonSerializer.Serialize(this, options);
}
public static ${model.type} Deserialize(string json)
public static ${model.type} Deserialize(string json, JsonSerializerOptions options = null)
{
var deserializeOptions = new JsonSerializerOptions();
deserializeOptions.Converters.Add(new ${model.name}Converter());
return JsonSerializer.Deserialize<${model.type}>(json, deserializeOptions);
return JsonSerializer.Deserialize<${model.type}>(json, options);
}`;
return `${content}\n${renderer.indent(supportFunctions)}`;
},
Expand All @@ -244,16 +244,100 @@ ${content}
internal class ${model.name}Converter : JsonConverter<${model.name}>
{
public override bool CanConvert(System.Type objectType)
{
// this converter can be applied to any type
return true;
}
${renderer.indent(deserialize)}
${renderer.indent(serialize)}
}`;
}
},
enum: {
self({content, renderer}) {
renderer.dependencyManager.addDependency('using System.Text.Json;')
return content;
},
extensionMethods({content, model, renderer}) {
const enums = model.values || [];
const items: string[] = [];
const items2: string[] = [];

for (const enumValue of enums) {
let jsonValue = enumValue.value;
const originalEnumValue = enumValue.originalInput;
let stringValue = jsonValue;
if(typeof jsonValue !== 'string') stringValue = `"${jsonValue}"`;
items.push(
`case ${model.name}.${enumValue.key}: return ${stringValue};`
);

if(typeof originalEnumValue === 'string'){
items2.push(
`if (value?.ValueKind == JsonValueKind.String && value?.GetString() == ${enumValue.value})
{
return ${model.name}.${enumValue.key};
}`
);
} else if(originalEnumValue === null){
items2.push(
`if (value == null || value?.ValueKind == JsonValueKind.Null && value?.GetRawText() == "null")
{
return ${model.name}.${enumValue.key};
}`
);
} else if(typeof originalEnumValue === 'boolean'){
items2.push(
`if (value?.ValueKind == JsonValueKind.True || value?.ValueKind == JsonValueKind.False && value?.GetBoolean() == ${enumValue.value})
{
return ${model.name}.${enumValue.key};
}`
);
} else if(Array.isArray(originalEnumValue)){
items2.push(
`if (value?.ValueKind == JsonValueKind.Array && value?.GetRawText() == ${enumValue.value})
{
return ${model.name}.${enumValue.key};
}`
);
} else if(typeof originalEnumValue === 'object'){
items2.push(
`if (value?.ValueKind == JsonValueKind.Object && value?.GetRawText() == ${enumValue.value})
{
return ${model.name}.${enumValue.key};
}`
);
} else if(typeof originalEnumValue === 'number'){
items2.push(
`if (value?.ValueKind == JsonValueKind.Number && value?.GetInt32() == ${enumValue.value})
{
return ${model.name}.${enumValue.key};
}`
);
} else if(typeof originalEnumValue === 'bigint'){
items2.push(
`if (value?.ValueKind == JsonValueKind.Number && value?.GetInt64() == ${enumValue.value})
{
return ${model.name}.${enumValue.key};
}`
);
}
}

const newstuff = items.join('\n');
const newstuff2 = items2.join('\n');
return `${content}
public static string? GetRawJsonValue(this ${model.name} enumValue)
{
switch (enumValue)
{
${renderer.indent(newstuff, 3)}
}
return null;
}
`;
}
public static ${model.type}? FromJsonTo${model.name}(JsonElement? value)
{
${renderer.indent(newstuff2, 2)}
return null;
}`;
},
}
};
70 changes: 41 additions & 29 deletions src/generators/csharp/renderers/EnumRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,15 @@ import { CSharpOptions } from '../CSharpGenerator';
export class EnumRenderer extends CSharpRenderer<ConstrainedEnumModel> {
async defaultSelf(): Promise<string> {
const enumItems = await this.renderItems();
const getValueCaseItemValues = this.getValueCaseItemValues();
const toEnumCaseItemValues = this.toEnumCaseItemValues();
const enumValueSwitch = `switch (enumValue)
{
${this.indent(getValueCaseItemValues)}
}
return null;`;
const valueSwitch = `switch (value)
{
${this.indent(toEnumCaseItemValues)}
}
return null;`;
const classContent = `public static ${this.model.type}? GetValue(this ${
this.model.name
} enumValue)
{
${this.indent(enumValueSwitch)}
}
public static ${this.model.name}? To${this.model.name}(dynamic? value)
{
${this.indent(valueSwitch)}
}`;
const extension = await this.runExtensionPreset();

return `public enum ${this.model.name}
{
${this.indent(enumItems)}
}
public static class ${this.model.name}Extensions
{
${this.indent(classContent)}
}
${this.indent(extension)}
`;
}

async renderItems(): Promise<string> {
const enums = this.model.values || [];
const items: string[] = [];
Expand Down Expand Up @@ -93,6 +66,12 @@ ${this.indent(classContent)}
runItemPreset(item: ConstrainedEnumValueModel): Promise<string> {
return this.runPreset('item', { item });
}
runExtensionPreset(): Promise<string> {
return this.runPreset('extension');
}
runExtensionMethodsPreset(): Promise<string> {
return this.runPreset('extensionMethods');
}
}

export const CSHARP_DEFAULT_ENUM_PRESET: EnumPresetType<CSharpOptions> = {
Expand All @@ -101,5 +80,38 @@ export const CSHARP_DEFAULT_ENUM_PRESET: EnumPresetType<CSharpOptions> = {
},
item({ item }) {
return item.key;
},
async extension({ renderer, model }) {
const extensionMethods = await renderer.runExtensionMethodsPreset();
return `public static class ${model.name}Extensions
{
${renderer.indent(extensionMethods)}
}`
},
extensionMethods({ model, renderer }) {
const getValueCaseItemValues = renderer.getValueCaseItemValues();
const toEnumCaseItemValues = renderer.toEnumCaseItemValues();
const enumValueSwitch = `switch (enumValue)
{
${renderer.indent(getValueCaseItemValues)}
}
return null;`;
const valueSwitch = `switch (value)
{
${renderer.indent(toEnumCaseItemValues)}
}
return null;`;
const classContent = `public static ${model.type}? GetValue(this ${
model.name
} enumValue)
{
${renderer.indent(enumValueSwitch)}
}
public static ${model.name}? To${model.name}(dynamic? value)
{
${renderer.indent(valueSwitch)}
}`;
return classContent;
}
};
1 change: 1 addition & 0 deletions src/generators/typescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export * from './TypeScriptFileGenerator';
export { TS_DEFAULT_PRESET } from './TypeScriptPreset';
export type { TypeScriptPreset } from './TypeScriptPreset';
export * from './presets';
export {RESERVED_TYPESCRIPT_KEYWORDS} from './Constants';

export {
defaultEnumKeyConstraints as typeScriptDefaultEnumKeyConstraints,
Expand Down
10 changes: 8 additions & 2 deletions src/generators/typescript/renderers/TypeRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ import { TypeScriptOptions } from '../TypeScriptGenerator';
* @extends TypeScriptRenderer
*/
export class TypeRenderer extends TypeScriptRenderer<ConstrainedMetaModel> {
defaultSelf(): string {
return `type ${this.model.name} = ${this.model.type};`;
async defaultSelf(): Promise<string> {
const content = [
await this.runAdditionalContentPreset()
];

return `type ${this.model.name} = ${this.model.type};
${this.renderBlock(content, 2)}`;
}
}

Expand Down
Loading

0 comments on commit a22a35a

Please sign in to comment.