feat: add typed production settings contract
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
using System.Globalization;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace PRecAnnot.Core;
|
||||
|
||||
/// <summary>Typed projection of the release Settings.xml sections consumed by production workflows.</summary>
|
||||
public sealed record ProductionSettingsContract(
|
||||
IReadOnlyList<NamedDrawingObject> CopyMissingObjects,
|
||||
IReadOnlyList<LayerSetupRule> LayerSetup,
|
||||
string AnnotationLayer,
|
||||
string AnnotationTextStyle,
|
||||
string AnnotationLeaderStyle,
|
||||
string ConnectionPolylineLayer,
|
||||
string ConnectionTextLayer,
|
||||
string ConnectionTextStyle,
|
||||
string ElevationLayer,
|
||||
string ElevationTextStyle,
|
||||
string SetLayer,
|
||||
double? FixedSupportPlusLength,
|
||||
string? ScriptFolder)
|
||||
{
|
||||
public static ProductionSettingsContract Parse(string xml)
|
||||
{
|
||||
var root = XElement.Parse(xml, LoadOptions.PreserveWhitespace);
|
||||
var copy = root.Element("CopyMissingObjects")?.Elements("Object")
|
||||
.Select(value => new NamedDrawingObject(Required(value, "type"), Required(value, "name")))
|
||||
.ToArray() ?? Array.Empty<NamedDrawingObject>();
|
||||
var layers = root.Element("LayersSetup")?.Elements("LayerSetup")
|
||||
.Select(value => new LayerSetupRule(
|
||||
Required(value, "filtertype"), Required(value, "filtervalue", allowEmpty: true),
|
||||
ParseInt(value, "lineweight"), Required(value, "linetype", allowEmpty: true),
|
||||
Required(value, "color", allowEmpty: true)))
|
||||
.ToArray() ?? Array.Empty<LayerSetupRule>();
|
||||
var annotations = root.Element("Annotations");
|
||||
var connections = root.Element("LinenumberConnections");
|
||||
var elevations = root.Element("Elevations");
|
||||
var setLayer = root.Element("SetLayer")?.Attribute("name")?.Value;
|
||||
if (string.IsNullOrWhiteSpace(setLayer)) throw Invalid("SetLayer.name");
|
||||
var plusLength = root.Element("PlusLengthNO")?.Attribute("value")?.Value;
|
||||
double? parsedPlusLength = double.TryParse(plusLength, NumberStyles.Float, CultureInfo.InvariantCulture, out var plus) ? plus : null;
|
||||
return new(
|
||||
copy,
|
||||
layers,
|
||||
Required(annotations, "layer"),
|
||||
Required(annotations, "textstyle"),
|
||||
Required(annotations, "multileaderstyle"),
|
||||
Required(connections, "polylinelayer"),
|
||||
Required(connections, "textlayer"),
|
||||
Required(connections, "textstyle"),
|
||||
Required(elevations, "layer"),
|
||||
Required(elevations, "textstyle"),
|
||||
setLayer,
|
||||
parsedPlusLength,
|
||||
root.Element("ScriptFolder")?.Attribute("path")?.Value);
|
||||
}
|
||||
|
||||
private static int? ParseInt(XElement value, string name) => int.TryParse(value.Attribute(name)?.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsed) ? parsed : null;
|
||||
private static string Required(XElement? value, string name, bool allowEmpty = false) => value?.Attribute(name) is { Value: var result } && (allowEmpty || !string.IsNullOrWhiteSpace(result)) ? result : throw Invalid(name);
|
||||
private static InvalidDataException Invalid(string field) => new("PRODUCTION_SETTINGS_INVALID:" + field);
|
||||
}
|
||||
|
||||
public sealed record NamedDrawingObject(string Type, string Name);
|
||||
public sealed record LayerSetupRule(string FilterType, string FilterValue, int? LineWeight, string LineType, string Color);
|
||||
@@ -0,0 +1,38 @@
|
||||
using PRecAnnot.Core;
|
||||
|
||||
namespace PRecAnnot.Core.Tests;
|
||||
|
||||
public sealed class ProductionSettingsContractTests
|
||||
{
|
||||
[Fact]
|
||||
public void ParsesReleaseSectionsAndPreservesAssetNames()
|
||||
{
|
||||
const string xml = """
|
||||
<Main>
|
||||
<CopyMissingObjects><Object type="Block" name="BLOCK_ELEVATION"/><Object type="Layer" name="TEXT"/></CopyMissingObjects>
|
||||
<PlusLengthNO value="1.5"/><ScriptFolder path="C:\PlantScripts"/>
|
||||
<LayersSetup><LayerSetup filtertype="Status" filtervalue="New" lineweight="60" linetype="Continuous" color=""/></LayersSetup>
|
||||
<Annotations layer="TEXT" textstyle="GOSTC" multileaderstyle="PXP"/>
|
||||
<LinenumberConnections polylinelayer="AXIS" textlayer="TEXT" textstyle="GOSTC"/>
|
||||
<Elevations layer="THIN" textstyle="GOSTC"/><SetLayer name="AXIS"/>
|
||||
</Main>
|
||||
""";
|
||||
|
||||
var result = ProductionSettingsContract.Parse(xml);
|
||||
|
||||
Assert.Equal("BLOCK_ELEVATION", result.CopyMissingObjects[0].Name);
|
||||
Assert.Equal(60, result.LayerSetup[0].LineWeight);
|
||||
Assert.Equal("PXP", result.AnnotationLeaderStyle);
|
||||
Assert.Equal("AXIS", result.ConnectionPolylineLayer);
|
||||
Assert.Equal(1.5, result.FixedSupportPlusLength);
|
||||
Assert.Equal("C:\\PlantScripts", result.ScriptFolder);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RejectsMissingRequiredSection()
|
||||
{
|
||||
const string xml = "<Main><SetLayer name=\"AXIS\"/></Main>";
|
||||
var exception = Assert.Throws<InvalidDataException>(() => ProductionSettingsContract.Parse(xml));
|
||||
Assert.Equal("PRODUCTION_SETTINGS_INVALID:layer", exception.Message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user