From 0a7f05989a557ae54b12888e2a2fd803823a6252 Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 7 Aug 2026 08:47:48 +0300 Subject: [PATCH] feat: add p-rec-annot line-number command preview service --- .../src/PRecAnnot.Abstractions/Contracts.cs | 1 + .../LineNumberCommandPreviewService.cs | 20 +++++++++++ .../LineNumberCommandPreviewServiceTests.cs | 35 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/LineNumberCommandPreviewService.cs create mode 100644 Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/LineNumberCommandPreviewServiceTests.cs diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Abstractions/Contracts.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Abstractions/Contracts.cs index eb4eb6a..5ea7b54 100644 --- a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Abstractions/Contracts.cs +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Abstractions/Contracts.cs @@ -62,6 +62,7 @@ public sealed record LineNumberCommandPlan(IReadOnlyList Rows); +public sealed record LineNumberPreviewResult(bool Success, LineNumberCommandPlan? Plan, IReadOnlyList Apply, string? FailureCode = null); public sealed record LineNumberTextAsset(string Layer, string TextStyle, double Size, string TextValue, Point2Dto Position); public sealed record LineNumberLineAsset(string Layer, Point2Dto Start, Point2Dto End); public sealed record LineNumberMarkerAsset(string Layer, double MaskScaleFactor, IReadOnlyList Texts, IReadOnlyList Lines); diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/LineNumberCommandPreviewService.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/LineNumberCommandPreviewService.cs new file mode 100644 index 0000000..f33c651 --- /dev/null +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/LineNumberCommandPreviewService.cs @@ -0,0 +1,20 @@ +using PRecAnnot.Abstractions; + +namespace PRecAnnot.Core; + +public sealed class LineNumberCommandPreviewService(LineNumberCommandPlanner planner, LineNumberFieldResolver fieldResolver) +{ + public LineNumberPreviewResult Preview(LineNumberAnnotationDefinition definition, IEnumerable candidates, IEnumerable existing) + { + var resolved = new List(); + foreach (var candidate in candidates.OrderBy(item => item.StableId, StringComparer.Ordinal)) + { + var fields = definition.Tags.Select(tag => tag.Field).Where(field => !string.IsNullOrWhiteSpace(field)); + var properties = fieldResolver.Resolve(candidate.RowId, fields); + if (!properties.Success) return new(false, null, Array.Empty(), properties.FailureCode); + resolved.Add(candidate with { Properties = properties.Values }); + } + var plan = planner.Build(definition, resolved, existing); + return new(true, plan, LineNumberCommandPlanner.PrepareApply(plan)); + } +} diff --git a/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/LineNumberCommandPreviewServiceTests.cs b/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/LineNumberCommandPreviewServiceTests.cs new file mode 100644 index 0000000..1d28e72 --- /dev/null +++ b/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/LineNumberCommandPreviewServiceTests.cs @@ -0,0 +1,35 @@ +using PRecAnnot.Abstractions; +using PRecAnnot.Core; + +namespace PRecAnnot.Core.Tests; + +public sealed class LineNumberCommandPreviewServiceTests +{ + [Fact] + public void UsesTheSameResolvedPropertiesForPreviewAndApply() + { + var reader = new FakeReader(new(true, new Dictionary { ["EngineeringItems.LineNumber"] = "L-42" })); + var service = new LineNumberCommandPreviewService(new(new LineNumberAnnotationFormatter()), new(reader)); + var definition = new LineNumberAnnotationDefinition { Nickname = "LN", Tags = [new() { Field = "EngineeringItems.LineNumber" }] }; + var candidate = new LineNumberCandidate("component", 42, new(1, 2), 0, "PXP", "Standard", new Dictionary()); + var result = service.Preview(definition, [candidate], Array.Empty()); + Assert.True(result.Success); + Assert.Equal("L-42", Assert.Single(result.Apply).Text); + Assert.Equal(42, reader.LastRowId); + } + + [Fact] + public void PropagatesTypedDataLinksFailureWithoutPlanning() + { + var service = new LineNumberCommandPreviewService(new(new LineNumberAnnotationFormatter()), new(new FakeReader(new(false, new Dictionary(), "DATALINKS_PROPERTY_READ_FAILED")))); + var definition = new LineNumberAnnotationDefinition { Nickname = "LN", Tags = [new() { Field = "EngineeringItems.LineNumber" }] }; + var candidate = new LineNumberCandidate("component", 42, new(), 0, "PXP", "Standard", new Dictionary()); + Assert.Equal("DATALINKS_PROPERTY_READ_FAILED", service.Preview(definition, [candidate], Array.Empty()).FailureCode); + } + + private sealed class FakeReader(DataLinksPropertyReadResult result) : IDataLinksPropertyReader + { + public long LastRowId { get; private set; } + public DataLinksPropertyReadResult ReadProperties(long rowId) { LastRowId = rowId; return result; } + } +}