fix: Fixes timer migrations not compiling (#902)

This commit is contained in:
Kamron Batman 2021-12-28 13:48:48 -08:00 committed by GitHub
parent d0aa6320f7
commit 5019ce9694
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 1273 additions and 1209 deletions

View file

@ -23,6 +23,13 @@ namespace SerializableMigration
{
string RuleName { get; }
void GenerateMigrationProperty(
StringBuilder source,
Compilation compilation,
string indent,
SerializableProperty serializableProperty
);
bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
@ -37,7 +44,8 @@ namespace SerializableMigration
StringBuilder source,
string indent,
SerializableProperty property,
string? parentReference
string? parentReference,
bool isMigration = false
);
void GenerateSerializationMethod(

View file

@ -18,117 +18,118 @@ using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;
namespace SerializableMigration
namespace SerializableMigration;
public class ArrayMigrationRule : MigrationRule
{
public class ArrayMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(ArrayMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(ArrayMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not IArrayTypeSymbol arrayTypeSymbol)
{
if (symbol is not IArrayTypeSymbol arrayTypeSymbol)
{
ruleArguments = null;
return false;
}
var serializableArrayType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ArrayEntry",
arrayTypeSymbol.ElementType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var length = serializableArrayType.RuleArguments?.Length?? 0;
ruleArguments = new string[length + 2];
ruleArguments[0] = arrayTypeSymbol.ElementType.ToDisplayString();
ruleArguments[1] = serializableArrayType.Rule;
if (length > 0)
{
Array.Copy(serializableArrayType.RuleArguments!, 0, ruleArguments, 2, length);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var serializableArrayType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ArrayEntry",
arrayTypeSymbol.ElementType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var length = serializableArrayType.RuleArguments?.Length?? 0;
ruleArguments = new string[length + 2];
ruleArguments[0] = arrayTypeSymbol.ElementType.ToDisplayString();
ruleArguments[1] = serializableArrayType.Rule;
if (length > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]];
var arrayElementRuleArguments = new string[ruleArguments.Length - 2];
Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2);
var propertyIndex = $"{property.Name}Index";
source.AppendLine($"{indent}{property.Name} = new {ruleArguments[0]}[reader.ReadEncodedInt()];");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {property.Name}.Length; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableArrayElement = new SerializableProperty
{
Name = $"{property.Name}[{propertyIndex}]",
Type = ruleArguments[0],
Rule = arrayElementRule.RuleName,
RuleArguments = arrayElementRuleArguments
};
arrayElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableArrayElement, parentReference);
source.AppendLine($"{indent}}}");
Array.Copy(serializableArrayType.RuleArguments!, 0, ruleArguments, 2, length);
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]];
var arrayElementRuleArguments = new string[ruleArguments.Length - 2];
Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyLength = $"{propertyVarPrefix}Length";
source.AppendLine($"{indent}var {propertyLength} = {property.Name}?.Length ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyLength});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyLength}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableArrayElement = new SerializableProperty
{
Name = $"{property.Name}![{propertyIndex}]",
Type = ruleArguments[0],
Rule = arrayElementRule.RuleName,
RuleArguments = arrayElementRuleArguments
};
arrayElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableArrayElement);
source.AppendLine($"{indent}}}");
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]];
var arrayElementRuleArguments = new string[ruleArguments.Length - 2];
Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2);
var propertyIndex = $"{property.Name}Index";
source.AppendLine($"{indent}{property.Name} = new {ruleArguments[0]}[reader.ReadEncodedInt()];");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {property.Name}.Length; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableArrayElement = new SerializableProperty
{
Name = $"{property.Name}[{propertyIndex}]",
Type = ruleArguments[0],
Rule = arrayElementRule.RuleName,
RuleArguments = arrayElementRuleArguments
};
arrayElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableArrayElement, parentReference);
source.AppendLine($"{indent}}}");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var arrayElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![1]];
var arrayElementRuleArguments = new string[ruleArguments.Length - 2];
Array.Copy(ruleArguments, 2, arrayElementRuleArguments, 0, ruleArguments.Length - 2);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyLength = $"{propertyVarPrefix}Length";
source.AppendLine($"{indent}var {propertyLength} = {property.Name}?.Length ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyLength});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyLength}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableArrayElement = new SerializableProperty
{
Name = $"{property.Name}![{propertyIndex}]",
Type = ruleArguments[0],
Rule = arrayElementRule.RuleName,
RuleArguments = arrayElementRuleArguments
};
arrayElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableArrayElement);
source.AppendLine($"{indent}}}");
}
}

View file

@ -20,230 +20,231 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class DictionaryMigrationRule : MigrationRule
{
public class DictionaryMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(DictionaryMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(DictionaryMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsDictionary(compilation))
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsDictionary(compilation))
{
ruleArguments = null;
return false;
}
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var serializableKeyProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"KeyEntry",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var serializableValueProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ValueEntry",
valueSymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
extraOptions += "@Tidy";
}
var keyArgumentsLength = serializableKeyProperty.RuleArguments?.Length ?? 0;
var valueArgumentsLength = serializableValueProperty.RuleArguments?.Length ?? 0;
var index = 0;
ruleArguments = new string[7 + keyArgumentsLength + valueArgumentsLength];
ruleArguments[index++] = extraOptions;
ruleArguments[index++] = keySymbolType.ToDisplayString();
ruleArguments[index++] = serializableKeyProperty.Rule;
ruleArguments[index++] = keyArgumentsLength.ToString();
if (keyArgumentsLength > 0)
{
Array.Copy(serializableKeyProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength);
index += keyArgumentsLength;
}
ruleArguments[index++] = valueSymbolType.ToDisplayString();
ruleArguments[index++] = serializableValueProperty.Rule;
ruleArguments[index++] = valueArgumentsLength.ToString();
if (valueArgumentsLength > 0)
{
Array.Copy(serializableValueProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var serializableKeyProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"KeyEntry",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var serializableValueProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ValueEntry",
valueSymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1;
var keyType = ruleArguments![index++];
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var valueType = ruleArguments[index++];
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[1]} {propertyKeyEntry};");
source.AppendLine($"{indent}{valueType} {propertyValueEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.Dictionary<{keyType}, {valueType}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = keyType,
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableKeyElement, parentReference);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = valueType,
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableValueElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyKeyEntry}, {propertyValueEntry});");
source.AppendLine($"{indent}}}");
extraOptions += "@Tidy";
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
var keyArgumentsLength = serializableKeyProperty.RuleArguments?.Length ?? 0;
var valueArgumentsLength = serializableValueProperty.RuleArguments?.Length ?? 0;
var index = 0;
ruleArguments = new string[7 + keyArgumentsLength + valueArgumentsLength];
ruleArguments[index++] = extraOptions;
ruleArguments[index++] = keySymbolType.ToDisplayString();
ruleArguments[index++] = serializableKeyProperty.Rule;
ruleArguments[index++] = keyArgumentsLength.ToString();
if (keyArgumentsLength > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 0;
var shouldTidy = ruleArguments![index++].Contains("@Tidy");
var keyType = ruleArguments![index++];
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var valueType = ruleArguments[index++];
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var ({propertyKeyEntry}, {propertyValueEntry}) in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = keyType,
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableKeyElement);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = valueType,
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableValueElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
Array.Copy(serializableKeyProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength);
index += keyArgumentsLength;
}
ruleArguments[index++] = valueSymbolType.ToDisplayString();
ruleArguments[index++] = serializableValueProperty.Rule;
ruleArguments[index++] = valueArgumentsLength.ToString();
if (valueArgumentsLength > 0)
{
Array.Copy(serializableValueProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength);
}
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1;
var keyType = ruleArguments![index++];
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var valueType = ruleArguments[index++];
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[1]} {propertyKeyEntry};");
source.AppendLine($"{indent}{valueType} {propertyValueEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.Dictionary<{keyType}, {valueType}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = keyType,
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableKeyElement, parentReference);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = valueType,
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableValueElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyKeyEntry}, {propertyValueEntry});");
source.AppendLine($"{indent}}}");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 0;
var shouldTidy = ruleArguments![index++].Contains("@Tidy");
var keyType = ruleArguments![index++];
var keyElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments![index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var valueType = ruleArguments[index++];
var valueElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyKeyEntry = $"{propertyVarPrefix}Key";
var propertyValueEntry = $"{propertyVarPrefix}Value";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var ({propertyKeyEntry}, {propertyValueEntry}) in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableKeyElement = new SerializableProperty
{
Name = propertyKeyEntry,
Type = keyType,
Rule = keyElementRule.RuleName,
RuleArguments = keyRuleArguments
};
keyElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableKeyElement);
var serializableValueElement = new SerializableProperty
{
Name = propertyValueEntry,
Type = valueType,
Rule = valueElementRule.RuleName,
RuleArguments = valueRuleArguments
};
valueElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableValueElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
}
}

View file

@ -19,54 +19,55 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class EnumMigrationRule : MigrationRule
{
public class EnumMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(EnumMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(EnumMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not ITypeSymbol typeSymbol || !typeSymbol.IsEnum())
{
if (symbol is not ITypeSymbol typeSymbol || !typeSymbol.IsEnum())
{
ruleArguments = null;
return false;
}
ruleArguments = Array.Empty<string>();
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
ruleArguments = Array.Empty<string>();
return true;
}
source.AppendLine($"{indent}{property.Name} = reader.ReadEnum<{property.Type}>();");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
source.AppendLine($"{indent}{property.Name} = reader.ReadEnum<{property.Type}>();");
}
source.AppendLine($"{indent}writer.WriteEnum<{property.Type}>({property.Name});");
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
source.AppendLine($"{indent}writer.WriteEnum<{property.Type}>({property.Name});");
}
}

View file

@ -20,151 +20,152 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class HashSetMigrationRule : MigrationRule
{
public class HashSetMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(HashSetMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(HashSetMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsHashSet(compilation))
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsHashSet(compilation))
{
ruleArguments = null;
return false;
}
var setTypeSymbol = namedTypeSymbol.TypeArguments[0];
var serializableSetType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"SetEntry",
setTypeSymbol,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
extraOptions += "@Tidy";
}
var length = serializableSetType.RuleArguments?.Length ?? 0;
ruleArguments = new string[length + 3];
ruleArguments[0] = extraOptions;
ruleArguments[1] = setTypeSymbol.ToDisplayString();
ruleArguments[2] = serializableSetType.Rule;
if (length > 0)
{
Array.Copy(serializableSetType.RuleArguments, 0, ruleArguments, 3, length);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var setTypeSymbol = namedTypeSymbol.TypeArguments[0];
var serializableSetType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"SetEntry",
setTypeSymbol,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var argumentsOffset = hasExtraOptions ? 1 : 0;
var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{property.Name} = new System.Collections.Generic.HashSet<{ruleArguments[argumentsOffset]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableSetElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = setElementRule.RuleName,
RuleArguments = setElementRuleArguments
};
setElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableSetElement, parentReference);
source.AppendLine($"{indent} {property.Name}.Add({propertyEntry});");
source.AppendLine($"{indent}}}");
extraOptions += "@Tidy";
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
var length = serializableSetType.RuleArguments?.Length ?? 0;
ruleArguments = new string[length + 3];
ruleArguments[0] = extraOptions;
ruleArguments[1] = setTypeSymbol.ToDisplayString();
ruleArguments[2] = serializableSetType.Rule;
if (length > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy");
var argumentsOffset = hasExtraOptions ? 1 : 0;
var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableSetElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = setElementRule.RuleName,
RuleArguments = setElementRuleArguments
};
setElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableSetElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
Array.Copy(serializableSetType.RuleArguments, 0, ruleArguments, 3, length);
}
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var argumentsOffset = hasExtraOptions ? 1 : 0;
var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{property.Name} = new System.Collections.Generic.HashSet<{ruleArguments[argumentsOffset]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableSetElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = setElementRule.RuleName,
RuleArguments = setElementRuleArguments
};
setElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableSetElement, parentReference);
source.AppendLine($"{indent} {property.Name}.Add({propertyEntry});");
source.AppendLine($"{indent}}}");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy");
var argumentsOffset = hasExtraOptions ? 1 : 0;
var setElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var setElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, setElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableSetElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = setElementRule.RuleName,
RuleArguments = setElementRuleArguments
};
setElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableSetElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
}
}

View file

@ -19,206 +19,207 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class KeyValuePairMigrationRule : MigrationRule
{
public class KeyValuePairMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(KeyValuePairMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(KeyValuePairMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsKeyValuePair(compilation))
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsKeyValuePair(compilation))
{
ruleArguments = null;
return false;
}
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var keySerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"key",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var valueSerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"value",
valueSymbolType,
1,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var keyArgumentsLength = keySerializedProperty.RuleArguments?.Length ?? 0;
var valueArgumentsLength = valueSerializedProperty.RuleArguments?.Length ?? 0;
var index = 0;
// Key
ruleArguments = new string[6 + keyArgumentsLength + valueArgumentsLength];
ruleArguments[index++] = ""; // Extra options
ruleArguments[index++] = keySymbolType.ToDisplayString();
ruleArguments[index++] = keySerializedProperty.Rule;
ruleArguments[index++] = keyArgumentsLength.ToString();
if (keyArgumentsLength > 0)
{
Array.Copy(keySerializedProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength);
index += keyArgumentsLength;
}
// Value
ruleArguments[index++] = valueSymbolType.ToDisplayString();
ruleArguments[index++] = valueSerializedProperty.Rule;
if (valueArgumentsLength > 0)
{
Array.Copy(valueSerializedProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var keySymbolType = namedTypeSymbol.TypeArguments[0];
var keySerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"key",
keySymbolType,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var valueSymbolType = namedTypeSymbol.TypeArguments[1];
var valueSerializedProperty = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"value",
valueSymbolType,
1,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var keyArgumentsLength = keySerializedProperty.RuleArguments?.Length ?? 0;
var valueArgumentsLength = valueSerializedProperty.RuleArguments?.Length ?? 0;
var index = 0;
// Key
ruleArguments = new string[6 + keyArgumentsLength + valueArgumentsLength];
ruleArguments[index++] = ""; // Extra options
ruleArguments[index++] = keySymbolType.ToDisplayString();
ruleArguments[index++] = keySerializedProperty.Rule;
ruleArguments[index++] = keyArgumentsLength.ToString();
if (keyArgumentsLength > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1; // skip extra options
var keyType = ruleArguments![index++];
var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var serializableKeyProperty = new SerializableProperty
{
Name = "key",
Type = keyType,
Rule = keyRule.RuleName,
RuleArguments = keyRuleArguments
};
keyRule.GenerateDeserializationMethod(
source,
indent,
serializableKeyProperty,
parentReference
);
var valueType = ruleArguments[index++];
var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var serializableValueProperty = new SerializableProperty
{
Name = "value",
Type = valueType,
Rule = valueRule.RuleName,
RuleArguments = valueRuleArguments
};
valueRule.GenerateDeserializationMethod(
source,
indent,
serializableValueProperty,
parentReference
);
source.AppendLine(
$"{indent}{property.Name} = new {SymbolMetadata.KEYVALUEPAIR_STRUCT}<{keyType}, {valueType}>(key, value);"
);
Array.Copy(keySerializedProperty.RuleArguments!, 0, ruleArguments, index, keyArgumentsLength);
index += keyArgumentsLength;
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
// Value
ruleArguments[index++] = valueSymbolType.ToDisplayString();
ruleArguments[index++] = valueSerializedProperty.Rule;
if (valueArgumentsLength > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1; // skip extra options
var keyType = ruleArguments![index++];
var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var serializableKeyProperty = new SerializableProperty
{
Name = $"{property.Name}.Key",
Type = keyType,
Rule = keyRule.RuleName,
RuleArguments = keyRuleArguments
};
keyRule.GenerateSerializationMethod(
source,
indent,
serializableKeyProperty
);
var valueType = ruleArguments[index++];
var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var serializableValueProperty = new SerializableProperty
{
Name = $"{property.Name}.Value",
Type = valueType,
Rule = valueRule.RuleName,
RuleArguments = valueRuleArguments
};
valueRule.GenerateSerializationMethod(
source,
indent,
serializableValueProperty
);
Array.Copy(valueSerializedProperty.RuleArguments!, 0, ruleArguments, index, valueArgumentsLength);
}
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1; // skip extra options
var keyType = ruleArguments![index++];
var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var serializableKeyProperty = new SerializableProperty
{
Name = "key",
Type = keyType,
Rule = keyRule.RuleName,
RuleArguments = keyRuleArguments
};
keyRule.GenerateDeserializationMethod(
source,
indent,
serializableKeyProperty,
parentReference
);
var valueType = ruleArguments[index++];
var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var serializableValueProperty = new SerializableProperty
{
Name = "value",
Type = valueType,
Rule = valueRule.RuleName,
RuleArguments = valueRuleArguments
};
valueRule.GenerateDeserializationMethod(
source,
indent,
serializableValueProperty,
parentReference
);
source.AppendLine(
$"{indent}{property.Name} = new {SymbolMetadata.KEYVALUEPAIR_STRUCT}<{keyType}, {valueType}>(key, value);"
);
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var index = 1; // skip extra options
var keyType = ruleArguments![index++];
var keyRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var keyRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (keyRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, keyRuleArguments, 0, keyRuleArguments.Length);
index += keyRuleArguments.Length;
}
var serializableKeyProperty = new SerializableProperty
{
Name = $"{property.Name}.Key",
Type = keyType,
Rule = keyRule.RuleName,
RuleArguments = keyRuleArguments
};
keyRule.GenerateSerializationMethod(
source,
indent,
serializableKeyProperty
);
var valueType = ruleArguments[index++];
var valueRule = SerializableMigrationRulesEngine.Rules[ruleArguments[index++]];
var valueRuleArguments = new string[int.Parse(ruleArguments[index++])];
if (valueRuleArguments.Length > 0)
{
Array.Copy(ruleArguments, index, valueRuleArguments, 0, valueRuleArguments.Length);
}
var serializableValueProperty = new SerializableProperty
{
Name = $"{property.Name}.Value",
Type = valueType,
Rule = valueRule.RuleName,
RuleArguments = valueRuleArguments
};
valueRule.GenerateSerializationMethod(
source,
indent,
serializableValueProperty
);
}
}

View file

@ -20,152 +20,153 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class ListMigrationRule : MigrationRule
{
public class ListMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(ListMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(ListMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsList(compilation))
{
if (symbol is not INamedTypeSymbol namedTypeSymbol || !symbol.IsList(compilation))
{
ruleArguments = null;
return false;
}
var listTypeSymbol = namedTypeSymbol.TypeArguments[0];
var serializableListType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ListEntry",
listTypeSymbol,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
extraOptions += "@Tidy";
}
var length = serializableListType.RuleArguments?.Length ?? 0;
ruleArguments = new string[length + 3];
ruleArguments[0] = extraOptions;
ruleArguments[1] = listTypeSymbol.ToDisplayString();
ruleArguments[2] = serializableListType.Rule;
if (length > 0)
{
Array.Copy(serializableListType.RuleArguments!, 0, ruleArguments, 3, length);
}
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
var listTypeSymbol = namedTypeSymbol.TypeArguments[0];
var serializableListType = SerializableMigrationRulesEngine.GenerateSerializableProperty(
compilation,
"ListEntry",
listTypeSymbol,
0,
attributes,
serializableTypes,
embeddedSerializableTypes,
parentSymbol,
null
);
var extraOptions = "";
if (attributes.Any(a => a.IsTidy(compilation)))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var argumentsOffset = hasExtraOptions ? 1 : 0;
var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[argumentsOffset + 1]];
var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.List<{ruleArguments[argumentsOffset]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableListElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = listElementRule.RuleName,
RuleArguments = listElementRuleArguments
};
listElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableListElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyEntry});");
source.AppendLine($"{indent}}}");
extraOptions += "@Tidy";
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
var length = serializableListType.RuleArguments?.Length ?? 0;
ruleArguments = new string[length + 3];
ruleArguments[0] = extraOptions;
ruleArguments[1] = listTypeSymbol.ToDisplayString();
ruleArguments[2] = serializableListType.Rule;
if (length > 0)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy");
var argumentsOffset = hasExtraOptions ? 1 : 0;
var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableListElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = listElementRule.RuleName,
RuleArguments = listElementRuleArguments
};
listElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableListElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
Array.Copy(serializableListType.RuleArguments!, 0, ruleArguments, 3, length);
}
return true;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var argumentsOffset = hasExtraOptions ? 1 : 0;
var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[argumentsOffset + 1]];
var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyIndex = $"{propertyVarPrefix}Index";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
source.AppendLine($"{indent}{ruleArguments[argumentsOffset]} {propertyEntry};");
source.AppendLine($"{indent}var {propertyCount} = reader.ReadEncodedInt();");
source.AppendLine($"{indent}{propertyName} = new System.Collections.Generic.List<{ruleArguments[argumentsOffset]}>({propertyCount});");
source.AppendLine($"{indent}for (var {propertyIndex} = 0; {propertyIndex} < {propertyCount}; {propertyIndex}++)");
source.AppendLine($"{indent}{{");
var serializableListElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = listElementRule.RuleName,
RuleArguments = listElementRuleArguments
};
listElementRule.GenerateDeserializationMethod(source, $"{indent} ", serializableListElement, parentReference);
source.AppendLine($"{indent} {propertyName}.Add({propertyEntry});");
source.AppendLine($"{indent}}}");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var ruleArguments = property.RuleArguments;
var hasExtraOptions = ruleArguments![0] == "" || ruleArguments[0].StartsWith("@", StringComparison.Ordinal);
var shouldTidy = hasExtraOptions && ruleArguments[0].Contains("@Tidy");
var argumentsOffset = hasExtraOptions ? 1 : 0;
var listElementRule = SerializableMigrationRulesEngine.Rules[ruleArguments[1 + argumentsOffset]];
var listElementRuleArguments = new string[ruleArguments.Length - 2 - argumentsOffset];
Array.Copy(ruleArguments, 2 + argumentsOffset, listElementRuleArguments, 0, ruleArguments.Length - 2 - argumentsOffset);
var propertyName = property.Name;
var propertyVarPrefix = $"{char.ToLower(propertyName[0])}{propertyName.Substring(1, propertyName.Length - 1)}";
var propertyEntry = $"{propertyVarPrefix}Entry";
var propertyCount = $"{propertyVarPrefix}Count";
if (shouldTidy)
{
source.AppendLine($"{indent}{property.Name}?.Tidy();");
}
source.AppendLine($"{indent}var {propertyCount} = {property.Name}?.Count ?? 0;");
source.AppendLine($"{indent}writer.WriteEncodedInt({propertyCount});");
source.AppendLine($"{indent}if ({propertyCount} > 0)");
source.AppendLine($"{indent}{{");
source.AppendLine($"{indent} foreach (var {propertyEntry} in {property.Name}!)");
source.AppendLine($"{indent} {{");
var serializableListElement = new SerializableProperty
{
Name = propertyEntry,
Type = ruleArguments[argumentsOffset],
Rule = listElementRule.RuleName,
RuleArguments = listElementRuleArguments
};
listElementRule.GenerateSerializationMethod(source, $"{indent} ", serializableListElement);
source.AppendLine($"{indent} }}");
source.AppendLine($"{indent}}}");
}
}

View file

@ -0,0 +1,36 @@
using System;
using System.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration;
public abstract class MigrationRule : ISerializableMigrationRule
{
public abstract string RuleName { get; }
public virtual void GenerateMigrationProperty(
StringBuilder source, Compilation compilation, string indent, SerializableProperty serializableProperty
)
{
var propertyType = serializableProperty.Type;
var type = compilation.GetTypeByMetadataName(propertyType)?.IsValueType == true
|| SymbolMetadata.IsPrimitiveFromTypeDisplayString(propertyType) && propertyType != "bool"
? $"{propertyType}{(serializableProperty.UsesSaveFlag == true ? "?" : "")}" : propertyType;
source.AppendLine($"{indent}internal readonly {type} {serializableProperty.Name};");
}
public abstract bool GenerateRuleState(
Compilation compilation, ISymbol symbol, ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes, ISymbol? parentSymbol, out string[] ruleArguments
);
public abstract void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
);
public abstract void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property);
}

View file

@ -20,129 +20,130 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class PrimitiveTypeMigrationRule : MigrationRule
{
public class PrimitiveTypeMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(PrimitiveTypeMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(PrimitiveTypeMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol.IsIpAddress(compilation) || symbol.IsTimeSpan(compilation))
{
if (symbol.IsIpAddress(compilation) || symbol.IsTimeSpan(compilation))
{
ruleArguments = Array.Empty<string>();
return true;
}
if (
symbol is not ITypeSymbol {
SpecialType: not (not
SpecialType.System_Boolean and not
SpecialType.System_SByte and not
SpecialType.System_Int16 and not
SpecialType.System_Int32 and not
SpecialType.System_Int64 and not
SpecialType.System_Byte and not
SpecialType.System_UInt16 and not
SpecialType.System_UInt32 and not
SpecialType.System_UInt64 and not
SpecialType.System_Single and not
SpecialType.System_Double and not
SpecialType.System_String and not
SpecialType.System_Decimal and not
SpecialType.System_DateTime)
} typeSymbol
)
{
ruleArguments = null;
return false;
}
ruleArguments = typeSymbol.SpecialType switch
{
SpecialType.System_Int32 when attributes.Any(a => a.IsEncodedInt(compilation)) =>
new[] { "EncodedInt" },
SpecialType.System_DateTime when attributes.Any(a => a.IsDeltaDateTime(compilation)) =>
new[] { "DeltaTime" },
SpecialType.System_String when attributes.Any(a => a.IsInternString(compilation)) =>
new[] { "InternString" },
_ => new[] { "" }
};
ruleArguments = Array.Empty<string>();
return true;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
if (
symbol is not ITypeSymbol {
SpecialType: not (not
SpecialType.System_Boolean and not
SpecialType.System_SByte and not
SpecialType.System_Int16 and not
SpecialType.System_Int32 and not
SpecialType.System_Int64 and not
SpecialType.System_Byte and not
SpecialType.System_UInt16 and not
SpecialType.System_UInt32 and not
SpecialType.System_UInt64 and not
SpecialType.System_Single and not
SpecialType.System_Double and not
SpecialType.System_String and not
SpecialType.System_Decimal and not
SpecialType.System_DateTime)
} typeSymbol
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null;
const string ipAddress = SymbolMetadata.IPADDRESS_CLASS;
const string timeSpan = SymbolMetadata.TIMESPAN_STRUCT;
const string date = "System.DateTime";
var readMethod = property.Type switch
{
"bool" => "ReadBool",
"sbyte" => "ReadSByte",
"short" => "ReadShort",
"int" when argument == "EncodedInt" => "ReadEncodedInt",
"int" => "ReadInt",
"long" => "ReadLong",
"byte" => "ReadByte",
"ushort" => "ReadUShort",
"uint" => "ReadUInt",
"ulong" => "ReadULong",
"float" => "ReadFloat",
"double" => "ReadDouble",
"string" => "ReadString",
"decimal" => "ReadDecimal",
date when argument == "DeltaTime" => "ReadDeltaTime",
date => "ReadDateTime",
ipAddress => "ReadIPAddress",
timeSpan => "ReadTimeSpan"
};
var readArgument = readMethod == "ReadString" && argument == "InternString" ? "true" : "";
source.AppendLine($"{indent}{propertyName} = reader.{readMethod}({readArgument});");
ruleArguments = null;
return false;
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
ruleArguments = typeSymbol.SpecialType switch
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
SpecialType.System_Int32 when attributes.Any(a => a.IsEncodedInt(compilation)) =>
new[] { "EncodedInt" },
SpecialType.System_DateTime when attributes.Any(a => a.IsDeltaDateTime(compilation)) =>
new[] { "DeltaTime" },
SpecialType.System_String when attributes.Any(a => a.IsInternString(compilation)) =>
new[] { "InternString" },
_ => new[] { "" }
};
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null;
return true;
}
var writeMethod = property.Type switch
{
"System.DateTime" when argument == "DeltaTime" => "WriteDeltaTime",
"int" when argument == "EncodedInt" => "WriteEncodedInt",
_ => "Write"
};
source.AppendLine($"{indent}writer.{writeMethod}({propertyName});");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null;
const string ipAddress = SymbolMetadata.IPADDRESS_CLASS;
const string timeSpan = SymbolMetadata.TIMESPAN_STRUCT;
const string date = "System.DateTime";
var readMethod = property.Type switch
{
"bool" => "ReadBool",
"sbyte" => "ReadSByte",
"short" => "ReadShort",
"int" when argument == "EncodedInt" => "ReadEncodedInt",
"int" => "ReadInt",
"long" => "ReadLong",
"byte" => "ReadByte",
"ushort" => "ReadUShort",
"uint" => "ReadUInt",
"ulong" => "ReadULong",
"float" => "ReadFloat",
"double" => "ReadDouble",
"string" => "ReadString",
"decimal" => "ReadDecimal",
date when argument == "DeltaTime" => "ReadDeltaTime",
date => "ReadDateTime",
ipAddress => "ReadIPAddress",
timeSpan => "ReadTimeSpan"
};
var readArgument = readMethod == "ReadString" && argument == "InternString" ? "true" : "";
source.AppendLine($"{indent}{propertyName} = reader.{readMethod}({readArgument});");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 ? property.RuleArguments[0] : null;
var writeMethod = property.Type switch
{
"System.DateTime" when argument == "DeltaTime" => "WriteDeltaTime",
"int" when argument == "EncodedInt" => "WriteEncodedInt",
_ => "Write"
};
source.AppendLine($"{indent}writer.{writeMethod}({propertyName});");
}
}

View file

@ -19,61 +19,62 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class PrimitiveUOTypeMigrationRule : MigrationRule
{
public class PrimitiveUOTypeMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(PrimitiveUOTypeMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(PrimitiveUOTypeMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
ruleArguments = symbol switch
{
ruleArguments = symbol switch
{
_ when symbol.IsPoint2D(compilation) => new[] { "Point2D" },
_ when symbol.IsPoint3D(compilation) => new[] { "Point3D" },
_ when symbol.IsRectangle2D(compilation) => new[] { "Rect2D" },
_ when symbol.IsRectangle3D(compilation) => new[] { "Rect3D" },
_ when symbol.IsRace(compilation) => new[] { "Race" },
_ when symbol.IsMap(compilation) => new[] { "Map" },
_ when symbol.IsBitArray(compilation) => new[] { "BitArray" },
_ => null
};
_ when symbol.IsPoint2D(compilation) => new[] { "Point2D" },
_ when symbol.IsPoint3D(compilation) => new[] { "Point3D" },
_ when symbol.IsRectangle2D(compilation) => new[] { "Rect2D" },
_ when symbol.IsRectangle3D(compilation) => new[] { "Rect3D" },
_ when symbol.IsRace(compilation) => new[] { "Race" },
_ when symbol.IsMap(compilation) => new[] { "Map" },
_ when symbol.IsBitArray(compilation) => new[] { "BitArray" },
_ => null
};
return ruleArguments != null;
return ruleArguments != null;
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = reader.Read{property.RuleArguments?[0] ?? ""}();");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = reader.Read{property.RuleArguments?[0] ?? ""}();");
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}writer.Write({propertyName});");
}
var propertyName = property.Name;
source.AppendLine($"{indent}writer.Write({propertyName});");
}
}

View file

@ -19,63 +19,64 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class RawSerializableMigrationRule : MigrationRule
{
public class RawSerializableMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(RawSerializableMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(RawSerializableMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is not ITypeSymbol typeSymbol)
{
if (symbol is not ITypeSymbol typeSymbol)
{
ruleArguments = null;
return false;
}
if (!typeSymbol.HasRawSerializableInterface(compilation, embeddedSerializableTypes))
{
ruleArguments = null;
return false;
}
ruleArguments = new[] { "" };
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
if (!typeSymbol.HasRawSerializableInterface(compilation, embeddedSerializableTypes))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = new {property.Type}({parentReference ?? "this"});");
source.AppendLine($"{indent}{propertyName}.Deserialize(reader);");
ruleArguments = null;
return false;
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
ruleArguments = new[] { "" };
return true;
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName}.Serialize(writer);");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = new {property.Type}({parentReference ?? "this"});");
source.AppendLine($"{indent}{propertyName}.Deserialize(reader);");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName}.Serialize(writer);");
}
}

View file

@ -19,56 +19,57 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class SerializableInterfaceMigrationRule : MigrationRule
{
public class SerializableInterfaceMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(SerializableInterfaceMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(SerializableInterfaceMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (symbol is ITypeSymbol typeSymbol && typeSymbol.HasSerializableInterface(compilation, serializableTypes))
{
if (symbol is ITypeSymbol typeSymbol && typeSymbol.HasSerializableInterface(compilation, serializableTypes))
{
ruleArguments = Array.Empty<string>();
return true;
}
ruleArguments = null;
return false;
ruleArguments = Array.Empty<string>();
return true;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
ruleArguments = null;
return false;
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = reader.ReadEntity<{property.Type}>();");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName} = reader.ReadEntity<{property.Type}>();");
}
var propertyName = property.Name;
source.AppendLine($"{indent}writer.Write({propertyName});");
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}writer.Write({propertyName});");
}
}

View file

@ -19,66 +19,67 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class SerializationMethodSignatureMigrationRule : MigrationRule
{
public class SerializationMethodSignatureMigrationRule : ISerializableMigrationRule
public override string RuleName => nameof(SerializationMethodSignatureMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(SerializationMethodSignatureMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if ((symbol as ITypeSymbol)?.HasPublicSerializeMethod(compilation, serializableTypes) != true)
{
if ((symbol as ITypeSymbol)?.HasPublicSerializeMethod(compilation, serializableTypes) != true)
{
ruleArguments = null;
return false;
}
if (symbol is not INamedTypeSymbol namedTypeSymbol ||
!namedTypeSymbol.HasGenericReaderCtor(compilation, parentSymbol, out var requiresParent))
{
ruleArguments = null;
return false;
}
ruleArguments = new[] { requiresParent ? "DeserializationRequiresParent" : "" };
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
if (symbol is not INamedTypeSymbol namedTypeSymbol ||
!namedTypeSymbol.HasGenericReaderCtor(compilation, parentSymbol, out var requiresParent))
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 &&
property.RuleArguments[0] == "DeserializationRequiresParent" ? ", this" : "";
source.AppendLine($"{indent}{propertyName} = new {property.Type}(reader{argument});");
ruleArguments = null;
return false;
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
ruleArguments = new[] { requiresParent ? "DeserializationRequiresParent" : "" };
return true;
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName}.Serialize(writer);");
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var argument = property.RuleArguments?.Length >= 1 &&
property.RuleArguments[0] == "DeserializationRequiresParent" ? ", this" : "";
source.AppendLine($"{indent}{propertyName} = new {property.Type}(reader{argument});");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
source.AppendLine($"{indent}{propertyName}.Serialize(writer);");
}
}

View file

@ -20,107 +20,117 @@ using System.Text;
using Microsoft.CodeAnalysis;
using SerializationGenerator;
namespace SerializableMigration
namespace SerializableMigration;
public class TimerMigrationRule : MigrationRule, IPostDeserializeMethod
{
public class TimerMigrationRule : ISerializableMigrationRule, IPostDeserializeMethod
public override string RuleName => nameof(TimerMigrationRule);
public override bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
{
public string RuleName => nameof(TimerMigrationRule);
public bool GenerateRuleState(
Compilation compilation,
ISymbol symbol,
ImmutableArray<AttributeData> attributes,
ImmutableArray<INamedTypeSymbol> serializableTypes,
ImmutableArray<INamedTypeSymbol> embeddedSerializableTypes,
ISymbol? parentSymbol,
out string[] ruleArguments
)
if (!(symbol is ITypeSymbol typeSymbol && typeSymbol.IsTimer(compilation)))
{
if (!(symbol is ITypeSymbol typeSymbol && typeSymbol.IsTimer(compilation)))
{
ruleArguments = null;
return false;
}
ruleArguments = attributes.Any(a => a.IsTimerDrift(compilation))
? new[] { "@TimerDrift" }
: new[] { "" };
return true;
ruleArguments = null;
return false;
}
public void GenerateDeserializationMethod(StringBuilder source, string indent, SerializableProperty property, string? parentReference)
ruleArguments = attributes.Any(a => a.IsTimerDrift(compilation))
? new[] { "@TimerDrift" }
: new[] { "" };
return true;
}
public override void GenerateMigrationProperty(
StringBuilder source, Compilation compilation, string indent, SerializableProperty serializableProperty
)
{
source.AppendLine($"{indent}internal readonly System.DateTime {serializableProperty.Name}Next;");
source.AppendLine($"{indent}internal readonly System.TimeSpan {serializableProperty.Name}Delay;");
}
public override void GenerateDeserializationMethod(
StringBuilder source, string indent, SerializableProperty property, string? parentReference, bool isMigration = false
)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var ruleArguments = property.RuleArguments;
var driftTimer = ruleArguments![0].Contains("@TimerDrift");
var readTimer = driftTimer ? "reader.ReadDeltaTime()" : "reader.ReadDateTime()";
source.AppendLine($"{indent}var {propertyName}Next = {readTimer};");
source.AppendLine($"{indent}var {propertyName}Delay = {propertyName}Next == System.DateTime.MinValue ? System.TimeSpan.MinValue : {propertyName}Next - Core.Now;");
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
var propertyName = property.Name;
var ruleArguments = property.RuleArguments;
var driftTimer = ruleArguments![0].Contains("@TimerDrift");
var readTimer = driftTimer ? "reader.ReadDeltaTime()" : "reader.ReadDateTime()";
var useVar = isMigration ? "" : "var ";
source.AppendLine($"{indent}{useVar}{propertyName}Next = {readTimer};");
source.AppendLine($"{indent}{useVar}{propertyName}Delay = {propertyName}Next == System.DateTime.MinValue ? System.TimeSpan.MinValue : {propertyName}Next - Core.Now;");
}
public override void GenerateSerializationMethod(StringBuilder source, string indent, SerializableProperty property)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
var expectedRule = RuleName;
var ruleName = property.Rule;
if (expectedRule != ruleName)
{
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
var propertyName = property.Name;
var ruleArguments = property.RuleArguments;
var driftTimer = ruleArguments![0].Contains("@TimerDrift");
var writerMethod = driftTimer ? "WriteDeltaTime" : "Write";
source.AppendLine($"{indent}writer.{writerMethod}({propertyName}?.Next ?? System.DateTime.MinValue);");
throw new ArgumentException($"Invalid rule applied to property {ruleName}. Expecting {expectedRule}, but received {ruleName}.");
}
public void PostDeserializeMethod(
StringBuilder source, string indent, SerializableProperty property, Compilation compilation, INamedTypeSymbol classSymbol
)
{
var deserializeTimerMethod = classSymbol
.GetMembers()
.OfType<IMethodSymbol>()
.FirstOrDefault(
m =>
var propertyName = property.Name;
var ruleArguments = property.RuleArguments;
var driftTimer = ruleArguments![0].Contains("@TimerDrift");
var writerMethod = driftTimer ? "WriteDeltaTime" : "Write";
source.AppendLine($"{indent}writer.{writerMethod}({propertyName}?.Next ?? System.DateTime.MinValue);");
}
public void PostDeserializeMethod(
StringBuilder source, string indent, SerializableProperty property, Compilation compilation, INamedTypeSymbol classSymbol
)
{
var deserializeTimerMethod = classSymbol
.GetMembers()
.OfType<IMethodSymbol>()
.FirstOrDefault(
m =>
{
if (!m.ReturnsVoid || m.Parameters.Length != 1 || !m.Parameters[0].Type.IsTimeSpan(compilation))
{
if (!m.ReturnsVoid || m.Parameters.Length != 1 || !m.Parameters[0].Type.IsTimeSpan(compilation))
{
return false;
}
return false;
}
return m.GetAttributes()
.FirstOrDefault(
attr =>
{
if (!SymbolEqualityComparer.Default.Equals(
return m.GetAttributes()
.FirstOrDefault(
attr =>
{
if (!SymbolEqualityComparer.Default.Equals(
attr.AttributeClass,
compilation.GetTypeByMetadataName(
SymbolMetadata.DESERIALIZE_TIMER_FIELD_ATTRIBUTE
)
))
{
return false;
}
var order = (int)attr.ConstructorArguments[0].Value!;
return order == property.Order;
{
return false;
}
) != null;
}
) ?? throw new Exception("Serializing a timer requires a method with the DeserializeTimerField attribute to handle creating the timer itself.");
source.AppendLine($"{indent}{deserializeTimerMethod.Name}({property.Name}Delay);");
}
var order = (int)attr.ConstructorArguments[0].Value!;
return order == property.Order;
}
) != null;
}
) ?? throw new Exception("Serializing a timer requires a method with the DeserializeTimerField attribute to handle creating the timer itself.");
source.AppendLine($"{indent}{deserializeTimerMethod.Name}({property.Name}Delay);");
}
}