Bläddra i källkod

ValueRange.FromString/IsEmpty、StringUtils.ToVersion

龙腾猫跃 4 månader sedan
förälder
incheckning
619aeac2f5

+ 1 - 3
Shared/Extensions/StringExtensions.cs

@@ -1,6 +1,4 @@
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System.Text.RegularExpressions;
+using System.Text.RegularExpressions;
 
 namespace MeloongCore.Extensions;
 public static class StringExtensions {

+ 1 - 1
Shared/General/Config.cs

@@ -48,7 +48,7 @@ public class JsonConfigProvider : IConfigProvider {
             json.Value[key] = JValue.CreateNull();
         } else if (encrypted) {
             // 需要加密,在 JSON 中保存密文字符串
-            json.Value[key] = CryptographyUtils.AesEncrypt(value is string str ? str : JsonConvert.SerializeObject(value)); // UNDONE: 加密改为识别码
+            json.Value[key] = CryptographyUtils.AesEncrypt(value is string str ? str : JsonConvert.SerializeObject(value)); // TODO: 加密改为使用识别码
         } else {
             // 用 JToken 保留原始结构
             json.Value[key] = JToken.FromObject(value);

+ 50 - 12
Shared/General/ValueRange.cs

@@ -9,6 +9,8 @@ public sealed class ValueRange<T>(
     bool hasLower, bool hasUpper
 ) where T : notnull, IComparable<T> {
 
+    #region 基础字段
+
     public T? Lower { get; private set; } = hasLower && lower is null ? throw new ArgumentNullException(nameof(lower)) : lower;
     public T? Upper { get; private set; } = hasUpper && upper is null ? throw new ArgumentNullException(nameof(upper)) : upper;
 
@@ -34,7 +36,9 @@ public sealed class ValueRange<T>(
     /// <summary> 上界值是否为闭区间。 </summary>
     public bool IsUpperInclusive = isUpperInclusive;
 
-    #region 工厂方法
+    #endregion
+
+    #region ClosedOpen、AtLeast 等工厂方法
 
     /// <summary> 创建等同于 <c>[lower, upper]</c> 的闭区间。 </summary>
     public static ValueRange<T> Closed(T lower, T upper) => new(lower, upper, true, true, true, true);
@@ -60,6 +64,41 @@ public sealed class ValueRange<T>(
 
     #endregion
 
+    #region 字符串双向转换
+
+    /// <summary>
+    /// 从字符串解析 <see cref="ValueRange{T}"/> 实例。
+    /// </summary>
+    public static ValueRange<T> FromString(string str, Func<string, T>? parseFunc = null) {
+        if (string.IsNullOrEmpty(str)) throw new ArgumentException("不能为空");
+        var isLowerInclusive = str[0] == '[';
+        var isUpperInclusive = str[^1] == ']';
+        if (!isLowerInclusive && str[0] != '(') throw new ArgumentException("开头必须是 [ 或 (");
+        if (!isUpperInclusive && str[^1] != ')') throw new ArgumentException("结尾必须是 ] 或 )");
+        var commaIndex = str.IndexOfAny([',', ',']);
+        if (commaIndex == -1) throw new ArgumentException("需要用逗号分隔两个值");
+        var lowerStr = str[1..commaIndex].Trim();
+        var upperStr = str[(commaIndex + 1)..^1].Trim();
+        T? lower = default;
+        T? upper = default;
+        bool hasLower = false;
+        bool hasUpper = false;
+        if (lowerStr is not ("" or "-∞")) {
+            lower = parseFunc != null ? parseFunc(lowerStr) : (T) Convert.ChangeType(lowerStr, typeof(T));
+            hasLower = true;
+        }
+        if (upperStr is not ("" or "+∞" or "∞")) {
+            upper = parseFunc != null ? parseFunc(upperStr) : (T) Convert.ChangeType(upperStr, typeof(T));
+            hasUpper = true;
+        }
+        return new(lower, upper, isLowerInclusive, isUpperInclusive, hasLower, hasUpper);
+    }
+
+    public override string ToString()
+        => $"{(HasLower && IsLowerInclusive ? "[" : "(")}{(HasLower ? Lower!.ToString() : "-∞")}, {(HasUpper ? Upper!.ToString() : "+∞")}{(HasUpper && IsUpperInclusive ? "]" : ")")}";
+
+    #endregion
+
     /// <summary>
     /// 判断指定值是否位于当前范围内。
     /// </summary>
@@ -77,12 +116,19 @@ public sealed class ValueRange<T>(
         return true;
     }
 
+    /// <summary>
+    /// 该范围是否不包含任何值。
+    /// </summary>
+    public bool IsEmpty() {
+        if (!HasLower || !HasUpper) return false;
+        int compared = Lower!.CompareTo(Upper!);
+        return compared > 0 || (compared == 0 && (!IsLowerInclusive || !IsUpperInclusive));
+    }
+
     /// <summary>
     /// 取当前范围与另一个范围的交集。
     /// </summary>
-    /// <returns>
-    /// 若两个范围存在交集,则返回交集范围;否则返回 <see langword="null" />。
-    /// </returns>
+    /// <returns> 若两个范围存在交集,则返回交集范围;否则返回 <see langword="null" />。 </returns>
     public ValueRange<T>? Intersect(ValueRange<T> other) {
         T? lower = default;
         T? upper = default;
@@ -137,15 +183,7 @@ public sealed class ValueRange<T>(
 
         var anyHasLower = HasLower || other.HasLower;
         var anyHasUpper = HasUpper || other.HasUpper;
-        if (anyHasLower && anyHasUpper) {
-            var compare = lower!.CompareTo(upper!);
-            if (compare > 0) return null;
-            if (compare == 0 && (!isLowerInclusive || !isUpperInclusive)) return null;
-        }
-
         return new(lower, upper, isLowerInclusive, isUpperInclusive, anyHasLower, anyHasUpper);
     }
 
-    public override string ToString()
-        => $"{(HasLower && IsLowerInclusive ? "[" : "(")}{(HasLower ? Lower!.ToString() : "-∞")}, {(HasUpper ? Upper!.ToString() : "+∞")}{(HasUpper && IsUpperInclusive ? "]" : ")")}";
 }

+ 13 - 1
Shared/Utils/TextUtils.cs → Shared/Utils/StringUtils.cs

@@ -1,7 +1,7 @@
 using System.Text.RegularExpressions;
 
 namespace MeloongCore;
-public static class TextUtils {
+public static class StringUtils {
 
     #region 转义
 
@@ -192,4 +192,16 @@ public static class TextUtils {
 
     #endregion
 
+    /// <summary>
+    /// 将字符串解析为版本号。
+    /// 允许缺少次要版本(如 "2" 代表 "2.0")。
+    /// </summary>
+    public static Version ToVersion(string value) {
+        if (Version.TryParse(value.Contains('.') ? value : value + ".0", out var version)) {
+            return version;
+        } else {
+            throw new FormatException($"无法将字符串 \"{value}\" 解析为版本号");
+        }
+    }
+
 }