feat: add FilenameTemplateParser with placeholder evaluation and validation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
96
ExportDXF/Services/FilenameTemplateParser.cs
Normal file
96
ExportDXF/Services/FilenameTemplateParser.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ExportDXF.Services
|
||||
{
|
||||
public static class FilenameTemplateParser
|
||||
{
|
||||
private static readonly Regex PlaceholderPattern = new Regex(
|
||||
@"\{(?<name>\w+)(?::(?<pad>\d+))?\}",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates a template string for a given item.
|
||||
/// e.g. "4321 A01 PT{item_no:2}" with item_no=3 → "4321 A01 PT03"
|
||||
/// </summary>
|
||||
public static string Evaluate(string template, Item item)
|
||||
{
|
||||
return PlaceholderPattern.Replace(template, match =>
|
||||
{
|
||||
var name = match.Groups["name"].Value.ToLowerInvariant();
|
||||
var padStr = match.Groups["pad"].Value;
|
||||
int pad = string.IsNullOrEmpty(padStr) ? 0 : int.Parse(padStr);
|
||||
|
||||
string value;
|
||||
switch (name)
|
||||
{
|
||||
case "item_no":
|
||||
value = item.ItemNo ?? "0";
|
||||
if (pad > 0)
|
||||
value = value.PadLeft(pad, '0');
|
||||
break;
|
||||
case "part_name":
|
||||
value = item.PartName ?? "";
|
||||
break;
|
||||
case "config":
|
||||
value = item.Configuration ?? "";
|
||||
break;
|
||||
case "material":
|
||||
value = item.Material ?? "";
|
||||
break;
|
||||
default:
|
||||
value = match.Value;
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the literal prefix before the first placeholder.
|
||||
/// Used for naming the xlsx and log files.
|
||||
/// Falls back to documentName if prefix is empty.
|
||||
/// </summary>
|
||||
public static string GetPrefix(string template, string documentName)
|
||||
{
|
||||
var match = PlaceholderPattern.Match(template);
|
||||
if (!match.Success)
|
||||
return template.Trim();
|
||||
|
||||
var prefix = template.Substring(0, match.Index).Trim();
|
||||
if (string.IsNullOrEmpty(prefix))
|
||||
return Path.GetFileNameWithoutExtension(documentName);
|
||||
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the template contains {item_no} to prevent filename collisions.
|
||||
/// </summary>
|
||||
public static bool Validate(string template, out string error)
|
||||
{
|
||||
error = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(template))
|
||||
{
|
||||
error = "Template cannot be empty.";
|
||||
return false;
|
||||
}
|
||||
|
||||
var hasItemNo = PlaceholderPattern.Matches(template)
|
||||
.Cast<Match>()
|
||||
.Any(m => m.Groups["name"].Value.Equals("item_no", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!hasItemNo)
|
||||
{
|
||||
error = "Template must contain {item_no} or {item_no:N} to avoid filename collisions.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user