Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 65 additions & 24 deletions Fody/ModuleWeaver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ void AddPropertyCode(MethodBody body, int index, PropertyDefinition property, Ty

NewStringBuilder(nt);

AppendString(nt, "[");
AppendString(nt, ListStart);

While(nt,
c =>
Expand Down Expand Up @@ -276,7 +276,7 @@ void AddPropertyCode(MethodBody body, int index, PropertyDefinition property, Ty
ins.Add(Instruction.Create(OpCodes.Pop));
});

AppendString(ins, "]");
AppendString(ins, ListEnd);
StringBuilderToString(ins);
},
nf =>
Expand Down Expand Up @@ -394,7 +394,7 @@ void AppendSeparator(Collection<Instruction> ins)
{
If(ins,
c => c.Add(Instruction.Create(OpCodes.Ldloc_3)),
t => AppendString(t, ", "),
t => AppendString(t, PropertiesSeparator),
e =>
{
ins.Add(Instruction.Create(OpCodes.Ldc_I4_1));
Expand All @@ -405,38 +405,47 @@ void AppendSeparator(Collection<Instruction> ins)
string GetFormatString(TypeDefinition type, PropertyDefinition[] properties)
{
var sb = new StringBuilder();
sb.Append("{{T: \"");
var offset = 0;
if (!type.HasGenericParameters)

if (WrapWithBrackets)
{
sb.Append(type.Name);
sb.Append("{{");
}
else

if (WriteTypeName)
{
var name = type.Name.Remove(type.Name.IndexOf('`'));
offset = type.GenericParameters.Count;
sb.Append(name);
sb.Append('<');
for (var i = 0; i < offset; i++)
sb.AppendFormat("T{0}\"", PropertyNameToValueSeparator);

if (!type.HasGenericParameters)
{
sb.Append(type.Name);
}
else
{
sb.Append("{");
sb.Append(i);
sb.Append("}");
if (i + 1 != offset)
var name = type.Name.Remove(type.Name.IndexOf('`'));
offset = type.GenericParameters.Count;
sb.Append(name);
sb.Append('<');
for (var i = 0; i < offset; i++)
{
sb.Append(", ");
sb.Append("{");
sb.Append(i);
sb.Append("}");
if (i + 1 != offset)
{
sb.Append(PropertiesSeparator);
}
}
sb.Append('>');
}
sb.Append('>');
sb.Append("\"" + PropertiesSeparator);
}
sb.Append("\", ");


for (var i = 0; i < properties.Length; i++)
{
var property = properties[i];
sb.Append(property.Name);
sb.Append(": ");
sb.Append(PropertyNameToValueSeparator);

if (HaveToAddQuotes(property.PropertyType))
{
Expand Down Expand Up @@ -464,10 +473,15 @@ string GetFormatString(TypeDefinition type, PropertyDefinition[] properties)

if (i != properties.Length - 1)
{
sb.Append(", ");
sb.Append(PropertiesSeparator);
}
}
sb.Append("}}");

if (WrapWithBrackets)
{
sb.Append("}}");
}

var format = sb.ToString();
return format;
}
Expand Down Expand Up @@ -507,4 +521,31 @@ PropertyDefinition[] RemoveIgnoredProperties(PropertyDefinition[] allProperties)
{
return allProperties.Where(x => x.CustomAttributes.All(y => y.AttributeType.Name != "IgnoreDuringToStringAttribute")).ToArray();
}
}

private string PropertyNameToValueSeparator => ReadStringValueFromConfig("PropertyNameToValueSeparator", ": ");

private string PropertiesSeparator => ReadStringValueFromConfig("PropertiesSeparator", ", ");

private string ListStart => ReadStringValueFromConfig("ListStart", "[");

private string ListEnd => ReadStringValueFromConfig("ListEnd", "]");

private bool WrapWithBrackets => ReadBoolValueFromConfig("WrapWithBrackets", true);

private bool WriteTypeName => ReadBoolValueFromConfig("WriteTypeName", true);

private string ReadStringValueFromConfig(string nodeName, string defaultValue)
{
var node = Config?.Attributes().FirstOrDefault(a => a.Name.LocalName == nodeName);
return node?.Value ?? defaultValue;
}

private bool ReadBoolValueFromConfig(string nodeName, bool defaultValue)
{
var node = Config?.Attributes().FirstOrDefault(a => a.Name.LocalName == nodeName);
bool nodeValue;
return node != null && bool.TryParse(node.Value, out nodeValue)
? nodeValue
: defaultValue;
}
}
9 changes: 9 additions & 0 deletions Tests/AttributesConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
public class AttributesConfiguration
{
public string PropertyNameToValueSeparator { get; set; }
public string PropertiesSeparator { get; set; }
public bool? WrapWithBrackets { get; set; }
public bool? WriteTypeName { get; set; }
public string ListStart { get; set; }
public string ListEnd { get; set; }
}
143 changes: 143 additions & 0 deletions Tests/AttributesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using NUnit.Framework;
using System;
using System.Reflection;

[TestFixture]
public class AttributesTests
{
private string PropertyNameToValueSeparator = "$%^%$";
private string PropertiesSeparator = "$@#@$";
private bool WrapWithBrackets = false;
private bool WriteTypeName = false;
private string ListStart = "---[[[";
private string ListEnd = "]]]---";

public Assembly PrepareAssembly(string name, AttributesConfiguration configuration)
{
var config = TestHelper.PrepareConfig(configuration);

var testSetup = TestHelper.PrepareDll(name);

var weavingTask = new ModuleWeaver
{
ModuleDefinition = testSetup.ModuleDefinition,
AssemblyResolver = testSetup.MockAssemblyResolver,
Config = config
};

weavingTask.Execute();
testSetup.ModuleDefinition.Write(testSetup.AfterAssemblyPath);

return Assembly.LoadFile(testSetup.AfterAssemblyPath);
}

[Test]
public void NormalClassTest_ShouldUseCustomPropertyNameToValueSeparator()
{
var assembly = PrepareAssembly("test1", new AttributesConfiguration { PropertyNameToValueSeparator = PropertyNameToValueSeparator });

var type = assembly.GetType("NormalClass");
dynamic instance = Activator.CreateInstance(type);
instance.X = 1;
instance.Y = "2";
instance.Z = 4.5;
instance.V = 'C';

var result = instance.ToString();

Assert.AreEqual(
string.Format("{{T{0}\"NormalClass\", X{0}1, Y{0}\"2\", Z{0}4.5, V{0}\"C\"}}", PropertyNameToValueSeparator),
result);
}

[Test]
public void NormalClassTest_ShouldUseCustomPropertiesSeparator()
{
var assembly = PrepareAssembly("test2", new AttributesConfiguration { PropertiesSeparator = PropertiesSeparator });

var type = assembly.GetType("NormalClass");
dynamic instance = Activator.CreateInstance(type);
instance.X = 1;
instance.Y = "2";
instance.Z = 4.5;
instance.V = 'C';

var result = instance.ToString();

Assert.AreEqual(
string.Format("{{T: \"NormalClass\"{0}X: 1{0}Y: \"2\"{0}Z: 4.5{0}V: \"C\"}}", PropertiesSeparator),
result);
}

[Test]
public void NormalClassTest_ShouldNotWrapInBrackets()
{
var assembly = PrepareAssembly("test3", new AttributesConfiguration { WrapWithBrackets = WrapWithBrackets });

var type = assembly.GetType("NormalClass");
dynamic instance = Activator.CreateInstance(type);
instance.X = 1;
instance.Y = "2";
instance.Z = 4.5;
instance.V = 'C';

var result = instance.ToString();

Assert.AreEqual(
"T: \"NormalClass\", X: 1, Y: \"2\", Z: 4.5, V: \"C\"",
result);
}

[Test]
public void NormalClassTest_ShouldNotWriteClassName()
{
var assembly = PrepareAssembly("test4", new AttributesConfiguration { WriteTypeName = WriteTypeName });

var type = assembly.GetType("NormalClass");
dynamic instance = Activator.CreateInstance(type);
instance.X = 1;
instance.Y = "2";
instance.Z = 4.5;
instance.V = 'C';

var result = instance.ToString();

Assert.AreEqual(
"{X: 1, Y: \"2\", Z: 4.5, V: \"C\"}",
result);
}

[Test]
public void NormalClassTest_ShouldStartListWithCustomSeparator()
{
var assembly = PrepareAssembly("test5", new AttributesConfiguration { ListStart = ListStart });

var type = assembly.GetType("IntCollection");
dynamic instance = Activator.CreateInstance(type);
instance.Collection = new[] { 1, 2, 3, 4, 5, 6 };
instance.Count = 2;

var result = instance.ToString();

var expected = $"{{T: \"IntCollection\", Count: 2, Collection: {ListStart}1, 2, 3, 4, 5, 6]}}";

Assert.AreEqual(expected, result);
}

[Test]
public void NormalClassTest_ShouldEndListWithCustomSeparator()
{
var assembly = PrepareAssembly("test6", new AttributesConfiguration { ListEnd = ListEnd });

var type = assembly.GetType("IntCollection");
dynamic instance = Activator.CreateInstance(type);
instance.Collection = new[] { 1, 2, 3, 4, 5, 6 };
instance.Count = 2;

var result = instance.ToString();

var expected = $"{{T: \"IntCollection\", Count: 2, Collection: [1, 2, 3, 4, 5, 6{ListEnd}}}";

Assert.AreEqual(expected, result);
}
}
Loading