Skip to content

Commit

Permalink
Merge pull request #220 from WeihanLi/dev
Browse files Browse the repository at this point in the history
1.0.70
  • Loading branch information
WeihanLi authored Oct 3, 2024
2 parents 19b1e3f + b05ab73 commit 8ad68f0
Show file tree
Hide file tree
Showing 25 changed files with 486 additions and 67 deletions.
1 change: 0 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RepositoryType>git</RepositoryType>
<Authors>WeihanLi</Authors>
<Copyright>Copyright 2017-$([System.DateTime]::Now.Year) (c) WeihanLi</Copyright>
<NoWarn>$(NoWarn);NU5048;CS1591;NETSDK1057</NoWarn>
Expand Down
10 changes: 5 additions & 5 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ExtensionPackageVersion Condition="'$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1' OR '$(TargetFramework)' == 'net6.0'">6.0.0</ExtensionPackageVersion>
<ExtensionPackageVersion Condition="'$(TargetFramework)' == 'net7.0'">7.0.0</ExtensionPackageVersion>
<ExtensionPackageVersion Condition="'$(TargetFramework)' == 'net8.0'">8.0.0</ExtensionPackageVersion>
<ExtensionPackageVersion Condition="'$(TargetFramework)' == 'net9.0'">9.0.0-preview.7.24405.7</ExtensionPackageVersion>
<ExtensionPackageVersion Condition="'$(TargetFramework)' == 'net9.0'">9.0.0-rc.1.24431.7</ExtensionPackageVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="$(ExtensionPackageVersion)" />
Expand All @@ -17,13 +17,13 @@
<PackageVersion Include="System.Reflection.Emit" Version="4.7.0" />
<PackageVersion Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Serilog" Version="4.0.1" />
<PackageVersion Include="Serilog" Version="4.0.2" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageVersion Include="FluentAssertions" Version="6.6.0" />
<PackageVersion Include="Moq" Version="[4.18.4]" />
<PackageVersion Include="xunit" Version="2.9.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="xunit" Version="2.9.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageVersion Include="Xunit.DependencyInjection" Version="8.7.1" />
<PackageVersion Include="Xunit.DependencyInjection.Logging" Version="8.1.0" />
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
- Logging Framework(结合Serilog/微软日志框架实现的日志框架)
- Dapper-like Ado.Net extensions(类似 Dapper 的 Ado.Net 扩展)
- TOTP implement(TOTP算法实现)
- Template Engine(自定义模板引擎)
- and more ...

## Release Notes
Expand Down
2 changes: 1 addition & 1 deletion build/version.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<PropertyGroup>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<VersionPatch>69</VersionPatch>
<VersionPatch>70</VersionPatch>
<VersionPrefix>$(VersionMajor).$(VersionMinor).$(VersionPatch)</VersionPrefix>
</PropertyGroup>
</Project>
4 changes: 3 additions & 1 deletion samples/DotNetCoreSample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,9 @@

// await InvokeHelper.TryInvokeAsync(EventTest.MainTest);

InvokeHelper.TryInvoke(CommandExecutorTest.MainTest);
// InvokeHelper.TryInvoke(CommandExecutorTest.MainTest);

await InvokeHelper.TryInvokeAsync(TemplatingSample.MainTest);

ConsoleHelper.ReadKeyWithPrompt("Press any key to exit");

Expand Down
2 changes: 2 additions & 0 deletions samples/DotNetCoreSample/TemplatingSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public static async Task MainTest()
var engine = TemplateEngine.CreateDefault();
var result = await engine.RenderAsync("Hello {{Name}}", new { Name = ".NET" });
Console.WriteLine(result);
Console.WriteLine(await engine.RenderAsync("Hello {{Name | toTitle }}", new { Name = "mike" }));
Console.WriteLine(await engine.RenderAsync("Today is {{ date | format:yyyy-MM-dd }}", new { date = DateTime.Today }));
}

{
Expand Down
11 changes: 6 additions & 5 deletions src/WeihanLi.Common/Extensions/CoreExtension.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System.Collections.Concurrent;
// Copyright (c) Weihan Li. All rights reserved.
// Licensed under the Apache license.

using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
Expand Down Expand Up @@ -1638,8 +1639,8 @@ public static bool ToBoolean(this string? value, bool defaultValue = false)
return value switch
{
null => defaultValue,
"" or "1" => true,
"0" => false,
"" or "1" or "y" or "yes" => true,
"0" or "n" or "no" => false,
_ => bool.TryParse(value, out var val) ? val : defaultValue
};
}
Expand Down
24 changes: 19 additions & 5 deletions src/WeihanLi.Common/Helpers/ApplicationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache license.

using System.Reflection;
using System.Runtime;
using System.Runtime.InteropServices;
using WeihanLi.Extensions;

Expand Down Expand Up @@ -42,6 +43,7 @@ public static LibraryInfo GetLibraryInfo(Assembly assembly)
var informationalVersionSplit = assemblyInformation.InformationalVersion.Split('+');
return new LibraryInfo()
{
VersionWithHash = assemblyInformation.InformationalVersion,
LibraryVersion = informationalVersionSplit[0],
LibraryHash = informationalVersionSplit.Length > 1 ? informationalVersionSplit[1] : string.Empty,
RepositoryUrl = repositoryUrl
Expand All @@ -55,8 +57,8 @@ public static LibraryInfo GetLibraryInfo(Assembly assembly)
};
}

private static readonly Lazy<RuntimeInfo> _runtimeInfoLazy = new(GetRuntimeInfo);
public static RuntimeInfo RuntimeInfo => _runtimeInfoLazy.Value;
private static readonly Lazy<RuntimeInfo> LazyRuntimeInfo = new(GetRuntimeInfo);
public static RuntimeInfo RuntimeInfo => LazyRuntimeInfo.Value;

/// <summary>
/// Get dotnet executable path
Expand Down Expand Up @@ -140,9 +142,6 @@ private static RuntimeInfo GetRuntimeInfo()
var runtimeInfo = new RuntimeInfo()
{
Version = Environment.Version.ToString(),
ProcessorCount = Environment.ProcessorCount,
FrameworkDescription = RuntimeInformation.FrameworkDescription,
WorkingDirectory = Environment.CurrentDirectory,

#if NET6_0_OR_GREATER
ProcessId = Environment.ProcessId,
Expand All @@ -152,18 +151,25 @@ private static RuntimeInfo GetRuntimeInfo()
ProcessId = currentProcess.Id,
ProcessPath = currentProcess.MainModule?.FileName ?? string.Empty,
#endif

ProcessorCount = Environment.ProcessorCount,
FrameworkDescription = RuntimeInformation.FrameworkDescription,
WorkingDirectory = Environment.CurrentDirectory,
OSArchitecture = RuntimeInformation.OSArchitecture.ToString(),
OSDescription = RuntimeInformation.OSDescription,
OSVersion = Environment.OSVersion.ToString(),
MachineName = Environment.MachineName,
UserName = Environment.UserName,

IsServerGC = GCSettings.IsServerGC,

IsInContainer = IsInContainer(),
IsInKubernetes = IsInKubernetesCluster(),
KubernetesNamespace = GetKubernetesNamespace(),

LibraryVersion = libInfo.LibraryVersion,
LibraryHash = libInfo.LibraryHash,
VersionWithHash = libInfo.VersionWithHash,
RepositoryUrl = libInfo.RepositoryUrl,
};
return runtimeInfo;
Expand Down Expand Up @@ -230,9 +236,11 @@ private static bool IsInKubernetesCluster()

public class LibraryInfo
{
private string? _versionWithHash;
public required string LibraryVersion { get; init; }
public required string LibraryHash { get; init; }
public required string RepositoryUrl { get; init; }
public string VersionWithHash { get => _versionWithHash ?? LibraryVersion; init => _versionWithHash = value; }
}

public class RuntimeInfo : LibraryInfo
Expand All @@ -250,6 +258,12 @@ public class RuntimeInfo : LibraryInfo
public required string RuntimeIdentifier { get; init; }
#endif

// GC
/// <summary>Gets a value that indicates whether server garbage collection is enabled.</summary>
/// <returns>
/// <see langword="true" /> if server garbage collection is enabled; otherwise, <see langword="false" />.</returns>
public required bool IsServerGC { get; init; }

public required string WorkingDirectory { get; init; }
public required int ProcessId { get; init; }
public required string ProcessPath { get; init; }
Expand Down
8 changes: 7 additions & 1 deletion src/WeihanLi.Common/Helpers/EnvHelper.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Weihan Li. All rights reserved.
// Licensed under the Apache license.

using System.Diagnostics.CodeAnalysis;
using WeihanLi.Extensions;

namespace WeihanLi.Common.Helpers;

Expand All @@ -12,4 +12,10 @@ public static class EnvHelper
{
return Environment.GetEnvironmentVariable(envName) ?? defaultValue;
}

public static bool BooleanVal(string envName, bool defaultValue = false)
{
var val = Environment.GetEnvironmentVariable(envName);
return val.ToBoolean(defaultValue);
}
}
14 changes: 9 additions & 5 deletions src/WeihanLi.Common/Template/ConfigurationRenderMiddleare.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@

namespace WeihanLi.Common.Template;

internal sealed class ConfigurationRenderMiddleware(IConfiguration? configuration = null) : IRenderMiddleware
internal sealed class ConfigurationRenderMiddleware(IConfiguration? configuration = null)
: IRenderMiddleware
{
private const string Prefix = "$config ";
private const string Prefix = "$config";
public Task InvokeAsync(TemplateRenderContext context, Func<TemplateRenderContext, Task> next)
{
if (configuration != null)
if (configuration is not null)
{
foreach (var variable in context.Variables.Where(x => x.StartsWith(Prefix) && !context.Parameters.ContainsKey(x)))
foreach (var pair in context.Inputs
.Where(x => x.Key.Prefix is Prefix
&& x.Value is null)
)
{
context.Parameters[variable] = configuration[variable[Prefix.Length..]];
context.Inputs[pair.Key] = configuration[pair.Key.VariableName];
}
}

Expand Down
18 changes: 17 additions & 1 deletion src/WeihanLi.Common/Template/DefaultRenderMiddleware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,26 @@

namespace WeihanLi.Common.Template;

internal sealed class DefaultRenderMiddleware : IRenderMiddleware
internal sealed class DefaultRenderMiddleware(Dictionary<string, ITemplatePipe> pipes) : IRenderMiddleware
{
public async Task InvokeAsync(TemplateRenderContext context, Func<TemplateRenderContext, Task> next)
{
await next(context);
foreach (var input in context.Inputs.Keys)
{
var value = context.Inputs[input];
if (input.Pipes is { Length: > 0 })
{
foreach (var pipeInput in input.Pipes)
{
if (pipes.TryGetValue(pipeInput.PipeName, out var pipe))
{
value = pipe.Convert(value, pipeInput.Arguments);
}
}
}
// replace input with value
context.RenderedText = context.RenderedText.Replace(input.Input, value as string ?? value?.ToString());
}
}
}
72 changes: 66 additions & 6 deletions src/WeihanLi.Common/Template/DefaultTemplateParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,79 @@ namespace WeihanLi.Common.Template;

internal sealed class DefaultTemplateParser : ITemplateParser
{
private const string VariableRegexExp = @"\{\{(?<Variable>[\w\$\s:]+)\}\}";
private static readonly Regex VariableRegex = new(VariableRegexExp, RegexOptions.Compiled);
private const string VariableGroupRegexExp = @"\{\{(?<Variable>[\w\$\s:\.]+)(?<Pipe>|[^\{\}]*)\}\}";
private static readonly Regex VariableRegex = new(VariableGroupRegexExp, RegexOptions.Compiled);
public Task<TemplateRenderContext> ParseAsync(string text)
{
var variables = new HashSet<string>();
List<TemplateInput> inputs = [];
var match = VariableRegex.Match(text);
while (match.Success)
{
var variable = match.Groups["Variable"].Value;
variables.Add(variable);
var pipes = Array.Empty<TemplatePipeInput>();
var variableInput = match.Groups["Variable"].Value;
var variableName = variableInput.Trim();
string? prefix = null;

var prefixIndex = variableName.IndexOf('$'); // prefix start
if (prefixIndex >= 0)
{
var nameIndex = variableName.IndexOf(' ', prefixIndex); // name start
prefix = variableName[..nameIndex].Trim();
variableName = variableName[nameIndex..].Trim();
}

var pipeValue = match.Groups["Pipe"]?.Value.Trim();
if (!string.IsNullOrEmpty(pipeValue))
{
var pipeIndex = pipeValue.IndexOf('|');

Check warning on line 34 in src/WeihanLi.Common/Template/DefaultTemplateParser.cs

View workflow job for this annotation

GitHub Actions / Running tests on ubuntu-latest

Dereference of a possibly null reference.

Check warning on line 34 in src/WeihanLi.Common/Template/DefaultTemplateParser.cs

View workflow job for this annotation

GitHub Actions / Running tests on macOS-latest

Dereference of a possibly null reference.

Check warning on line 34 in src/WeihanLi.Common/Template/DefaultTemplateParser.cs

View workflow job for this annotation

GitHub Actions / Running tests on windows-latest

Dereference of a possibly null reference.
if (pipeIndex < 0)
{
match = match.NextMatch();
continue;
}

// exact pipes
pipeValue = pipeValue[pipeIndex..].Trim();
var pipeInputs = pipeValue!.Split(['|'], StringSplitOptions.RemoveEmptyEntries);
pipes = pipeInputs.Select(p =>
{
var pipeName = p.Trim();
var arguments = Array.Empty<string>();
var sep = pipeName.IndexOf(':');
if (sep >= 0)
{
if (sep + 1 < pipeName.Length)
{
var argumentsText = pipeName[(sep + 1)..].Trim();
arguments =
#if NET
argumentsText.Split([':'],
StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
#else
argumentsText.Split([':'], StringSplitOptions.RemoveEmptyEntries)
.Select(x=> x.Trim()).ToArray();
#endif
}

pipeName = pipeName[..sep].Trim();
}

return new TemplatePipeInput() { PipeName = pipeName, Arguments = arguments };
}).ToArray();
}

var input = new TemplateInput
{
Input = match.Value,
Prefix = prefix,
VariableName = variableName,
Pipes = pipes
};
inputs.Add(input);

match = match.NextMatch();
}
var context = new TemplateRenderContext(text, variables);
var context = new TemplateRenderContext(text, inputs);
return Task.FromResult(context);
}
}
23 changes: 14 additions & 9 deletions src/WeihanLi.Common/Template/DefaultTemplateRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,26 @@

namespace WeihanLi.Common.Template;

internal sealed class DefaultTemplateRenderer(Func<TemplateRenderContext, Task> renderFunc) : ITemplateRenderer
internal sealed class DefaultTemplateRenderer(Func<TemplateRenderContext, Task> renderFunc)
: ITemplateRenderer
{
public async Task<string> RenderAsync(TemplateRenderContext context, object? globals)
{
if (context.Text.IsNullOrWhiteSpace() || context.Variables.IsNullOrEmpty())
if (context.Text.IsNullOrWhiteSpace() || context.Inputs.IsNullOrEmpty())
return context.Text;

context.Parameters = globals.ParseParamDictionary();
await renderFunc.Invoke(context).ConfigureAwait(false);
foreach (var parameter in context.Parameters)

var parameters = globals.ParseParamDictionary();
if (parameters is { Count: > 0 })
{
context.RenderedText = context.RenderedText.Replace(
$"{{{{{parameter.Key}}}}}", parameter.Value?.ToString()
);
foreach (var input in context.Inputs.Keys.Where(x => x.Prefix is null))
{
if (parameters.TryGetValue(input.VariableName, out var value))
{
context.Inputs[input] = value;
}
}
}
await renderFunc.Invoke(context).ConfigureAwait(false);
return context.RenderedText;
}
}
Loading

0 comments on commit 8ad68f0

Please sign in to comment.