Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0.70 #220

Merged
merged 20 commits into from
Oct 3, 2024
Merged
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
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 @@

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 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 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 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 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 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
Loading