手册数值改为占位符,渲染时从 Tuning 取值;加规则文件和测试
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
38f9ea9daf
commit
d28ca4e50c
6 files changed
+94
-3
No files matched your search
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Decentralized.Domain
|
||||
{
|
||||
/// <summary>
|
||||
/// 文本里的数值占位符:<c>{Name}</c>、<c>{Name:%}</c>、<c>{Name[2]:%}</c>,也可以走嵌套类 <c>{Section.Name}</c>。
|
||||
/// 手册等说明文字不直接写数字,渲染时从 <see cref="Tuning"/> 取值,数值一改文字跟着变。
|
||||
/// <c>%</c> 把小数按百分比显示(0.03 → 3%);不带格式时去掉多余的零。
|
||||
/// </summary>
|
||||
public static class TuningText
|
||||
{
|
||||
private static readonly Regex Placeholder = new Regex(@"\{([A-Za-z0-9_.]+)(?:\[(\d+)\])?(?::(%))?\}", RegexOptions.Compiled);
|
||||
|
||||
public static string Render(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return text;
|
||||
return Placeholder.Replace(text, m => TryValue(m.Groups[1].Value, m.Groups[2].Success ? int.Parse(m.Groups[2].Value) : -1, out var v)
|
||||
? Format(v, m.Groups[3].Success)
|
||||
: m.Value);
|
||||
}
|
||||
|
||||
/// <summary>文本里取不到值的占位符(测试用)。</summary>
|
||||
public static System.Collections.Generic.List<string> Unresolved(string text)
|
||||
{
|
||||
var bad = new System.Collections.Generic.List<string>();
|
||||
foreach (Match m in Placeholder.Matches(text ?? ""))
|
||||
if (!TryValue(m.Groups[1].Value, m.Groups[2].Success ? int.Parse(m.Groups[2].Value) : -1, out _)) bad.Add(m.Value);
|
||||
return bad;
|
||||
}
|
||||
|
||||
private static string Format(double v, bool percent)
|
||||
{
|
||||
if (percent) v *= 100;
|
||||
return Math.Round(v, 4).ToString("0.####", CultureInfo.InvariantCulture) + (percent ? "%" : "");
|
||||
}
|
||||
|
||||
private static bool TryValue(string path, int index, out double value)
|
||||
{
|
||||
value = 0;
|
||||
var type = typeof(Tuning);
|
||||
var parts = path.Split('.');
|
||||
for (int i = 0; i < parts.Length - 1; i++)
|
||||
{
|
||||
type = type.GetNestedType(parts[i], BindingFlags.Public | BindingFlags.Static);
|
||||
if (type == null) return false;
|
||||
}
|
||||
var field = type.GetField(parts[parts.Length - 1], BindingFlags.Public | BindingFlags.Static);
|
||||
if (field == null) return false;
|
||||
var raw = field.GetValue(null);
|
||||
if (raw is Array arr)
|
||||
{
|
||||
if (index < 0 || index >= arr.Length) return false;
|
||||
raw = arr.GetValue(index);
|
||||
}
|
||||
else if (index >= 0) return false;
|
||||
try { value = Convert.ToDouble(raw, CultureInfo.InvariantCulture); return true; }
|
||||
catch (Exception) { return false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user