Skip to content

Commit

Permalink
Improve support for invalid identifier characters
Browse files Browse the repository at this point in the history
This brings parity with the built-in resource generator, which properly
converts |, :, ;, < and > to underscores in the generated member
names.

Fixes #125
  • Loading branch information
kzu committed Oct 15, 2022
1 parent 2bcf5a0 commit 668990f
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 21 deletions.
12 changes: 6 additions & 6 deletions src/ThisAssembly.Strings/CSharp.sbntxt
Original file line number Diff line number Diff line change
Expand Up @@ -18,35 +18,35 @@
/// </summary>
{{ end }}
{{ func render }}
public static partial class {{ $0.Name }}
public static partial class {{ $0.Id }}
{
{{~ for value in $0.Values ~}}
{{~ if!(value.HasFormat) ~}}
{{- summary value ~}}
public static string {{ value.Name }} => Strings.GetResourceManager("{{ $1 }}").GetString("{{ $0.Prefix + value.Name }}");
public static string {{ value.Id }} => Strings.GetResourceManager("{{ $1 }}").GetString("{{ value.Name }}");
{{~ else ~}}
{{~ if value.IsIndexedFormat ~}}
{{- summary value ~}}
public static string {{ value.Name }}(
public static string {{ value.Id }}(
{{- for arg in value.Format -}}
object arg{{~ arg ~}}{{ if !for.last }}, {{ end }}
{{- end -}})
=> string.Format(
CultureInfo.CurrentCulture,
Strings.GetResourceManager("{{ $1 }}").GetString("{{ $0.Prefix + value.Name }}"),
Strings.GetResourceManager("{{ $1 }}").GetString("{{ value.Name }}"),
{{ for arg in value.Format -}}
arg{{- arg -}}{{- if !for.last -}}, {{ end }}{{- end -}});
{{~ else if value.IsNamedFormat ~}}
{{- summary value ~}}
public static string {{ value.Name }}(
public static string {{ value.Id }}(
{{- for arg in value.Format -}}
object {{ arg ~}}{{ if !for.last }}, {{ end }}
{{- end -}})
=> string.Format(
CultureInfo.CurrentCulture,
Strings
.GetResourceManager("{{ $1 }}")
.GetString("{{ $0.Prefix + value.Name }}")
.GetString("{{ value.Name }}")
{{- for arg in value.Format }}
.Replace("{%{{}%}{{ arg }}{%{}}%}", "{%{{}%}{{ for.index }}{%{}}%}"){{- end -}},
{{ for arg in value.Format -}}
Expand Down
24 changes: 13 additions & 11 deletions src/ThisAssembly.Strings/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ record Model(ResourceArea RootArea, string ResourceName)
static class ResourceFile
{
static readonly Regex FormatExpression = new Regex("{(?<name>[^{}]+)}", RegexOptions.Compiled);
internal static readonly Regex NameReplaceExpression = new Regex(@"\||:|;|\>|\<", RegexOptions.Compiled);

public static ResourceArea Load(string fileName, string rootArea)
{
Expand All @@ -33,17 +34,18 @@ public static ResourceArea Load(IEnumerable<XElement> data, string rootArea)
{
// Splits: ([resouce area]_)*[resouce name]
var nameAttribute = element.Attribute("name").Value;
var id = NameReplaceExpression.Replace(nameAttribute, "_");
var valueElement = element.Element("value").Value;
var comment = element.Element("comment")?.Value?.Replace("<", "&lt;").Replace(">", "&gt;");
var areaParts = nameAttribute.Split(new[] { "_" }, StringSplitOptions.RemoveEmptyEntries);
var areaParts = id.Split(new[] { "_" }, StringSplitOptions.RemoveEmptyEntries);
if (areaParts.Length <= 1)
{
root.Values.Add(GetValue(nameAttribute, valueElement) with { Comment = comment });
root.Values.Add(GetValue(id, nameAttribute, valueElement) with { Comment = comment });
}
else
{
var area = GetArea(root, areaParts.Take(areaParts.Length - 1));
var value = GetValue(areaParts.Skip(areaParts.Length - 1).First(), valueElement) with { Comment = comment };
var value = GetValue(areaParts.Skip(areaParts.Length - 1).First(), nameAttribute, valueElement) with { Comment = comment };

area.Values.Add(value);
}
Expand All @@ -65,13 +67,13 @@ static ResourceArea GetArea(ResourceArea area, IEnumerable<string> areaPath)
var currentArea = area;
foreach (var areaName in areaPath)
{
var existing = currentArea.NestedAreas.FirstOrDefault(a => a.Name == areaName);
var existing = currentArea.NestedAreas.FirstOrDefault(a => a.Id == areaName);
if (existing == null)
{
if (currentArea.Values.Any(v => v.Name == areaName))
throw new ArgumentException(string.Format(
"Area name '{0}' is already in use as a value name under area '{1}'.",
areaName, currentArea.Name));
areaName, currentArea.Id));

existing = new ResourceArea(areaName, currentArea.Prefix + areaName + "_");
currentArea.NestedAreas.Add(existing);
Expand All @@ -83,9 +85,9 @@ static ResourceArea GetArea(ResourceArea area, IEnumerable<string> areaPath)
return currentArea;
}

static ResourceValue GetValue(string resourceName, string resourceValue)
static ResourceValue GetValue(string resourceId, string resourceName, string resourceValue)
{
var value = new ResourceValue(resourceName, resourceValue);
var value = new ResourceValue(resourceId, resourceName, resourceValue);

// Parse parameter names
if (FormatExpression.IsMatch(resourceValue))
Expand All @@ -101,15 +103,15 @@ static ResourceValue GetValue(string resourceName, string resourceValue)
}
}

[DebuggerDisplay("Name = {Name}, NestedAreas = {NestedAreas.Count}, Values = {Values.Count}")]
record ResourceArea(string Name, string Prefix)
[DebuggerDisplay("Id = {Id}, NestedAreas = {NestedAreas.Count}, Values = {Values.Count}")]
record ResourceArea(string Id, string Prefix)
{
public List<ResourceArea> NestedAreas { get; init; } = new List<ResourceArea>();
public List<ResourceValue> Values { get; init; } = new List<ResourceValue>();
}

[DebuggerDisplay("{Name} = {Value}")]
record ResourceValue(string Name, string? Raw)
[DebuggerDisplay("{Id} = {Value}")]
record ResourceValue(string Id, string Name, string? Raw)
{
public string? Value => Raw?.Replace(Environment.NewLine, "")?.Replace("<", "&lt;")?.Replace(">", "&gt;");
public string? Comment { get; init; }
Expand Down
2 changes: 1 addition & 1 deletion src/ThisAssembly.Strings/ThisAssembly.Strings.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ such as "Hello {name}".
<ItemGroup>
<EmbeddedResource Include="ThisAssembly.Strings.cs" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion src/ThisAssembly.Tests/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Foo_Bar_Baz" xml:space="preserve">
<data name="Foo:Bar&gt;Baz" xml:space="preserve">
<value>Value</value>
</data>
<data name="Foo_Hey" xml:space="preserve">
Expand Down
8 changes: 6 additions & 2 deletions src/ThisAssembly.Tests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@ namespace ThisAssemblyTests
{
public class Tests
{
[Fact]
public void CanReadResourceFile()
=> Assert.NotNull(ResourceFile.Load("Resources.resx", "Strings"));

[Fact]
public void CanUseConstants()
=> Assert.Equal("Baz", ThisAssembly.Constants.Foo.Bar);

[Fact]
public void CanUseFileConstants()
=> Assert.Equal(Path.Combine("Content", "Docs", "License.md"), ThisAssembly.Constants.Content.Docs.License);
=> Assert.Equal(ThisAssembly.Constants.Content.Docs.License, Path.Combine("Content", "Docs", "License.md"));

[Fact]
public void CanUseFileConstantLinkedFile()
=> Assert.Equal(Path.Combine("Included", "Readme.txt"), ThisAssembly.Constants.Included.Readme);
=> Assert.Equal(ThisAssembly.Constants.Included.Readme, Path.Combine("Included", "Readme.txt"));

[Fact]
public void CanUseMetadata()
Expand Down
4 changes: 4 additions & 0 deletions src/ThisAssembly.Tests/ThisAssembly.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
</FileConstant>
<AssemblyMetadata Include="Foo" Value="Bar" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\ThisAssembly.Strings\Model.cs" Link="Model.cs" />
</ItemGroup>

<Target Name="GetDependencyTargetPaths" Returns="@(TargetPathWithTargetPlatformMoniker)">
<ItemGroup>
Expand Down

0 comments on commit 668990f

Please sign in to comment.