src/SSC/ParallelDiffPathPattern.cs

実行済み 128 · 未実行 7 · 対象外 217
Coverage source: 043e27b9e5febd713c747d2558802e450fad8edd

実行済みCoberturaのhit数が1以上の行です。
未実行カバレッジ対象ですがhit数が0の行です。
対象外Coberturaに実行対象として記録されていない行です。
行状態HitsSource
1 対象外 using SSC.Internal;
2 対象外
3 対象外 namespace SSC;
4 対象外
5 対象外 /// <summary>
6 対象外 /// <see cref="ParallelDiffEntry.Path"/> と照合する XPath-like path pattern を表します。
7 対象外 /// </summary>
8 対象外 public sealed class ParallelDiffPathPattern
9 対象外 {
10 対象外 private readonly IReadOnlyList<PatternSegment> _segments;
11 対象外
12 実行済み 35 private ParallelDiffPathPattern(IReadOnlyList<PatternSegment> segments)
13 対象外 {
14 実行済み 35 _segments = segments;
15 実行済み 35 }
16 対象外
17 対象外 /// <summary>
18 対象外 /// 指定した文字列を差分 path pattern として解析します。
19 対象外 /// </summary>
20 対象外 /// <param name="pattern">解析する root-relative path pattern。<see langword="null"/> の場合は解析に失敗します。</param>
21 対象外 /// <returns>解析済み pattern。</returns>
22 対象外 /// <exception cref="ArgumentNullException"><paramref name="pattern"/> が <see langword="null"/> の場合。</exception>
23 対象外 /// <exception cref="FormatException"><paramref name="pattern"/> が pattern grammar に適合しない場合。</exception>
24 対象外 public static ParallelDiffPathPattern Parse(string pattern)
25 対象外 {
26 実行済み 37 ArgumentNullException.ThrowIfNull(pattern);
27 対象外
28 実行済み 36 if (!TryParse(pattern, out var parsedPattern) || parsedPattern is null)
29 対象外 {
30 実行済み 1 throw new FormatException($"Diff path pattern '{pattern}' is invalid.");
31 対象外 }
32 対象外
33 実行済み 35 return parsedPattern;
34 対象外 }
35 対象外
36 対象外 /// <summary>
37 対象外 /// 指定した文字列を差分 path pattern として解析します。
38 対象外 /// </summary>
39 対象外 /// <param name="pattern">解析する root-relative path pattern。</param>
40 対象外 /// <param name="parsedPattern">解析に成功した場合の pattern。</param>
41 対象外 /// <returns>解析に成功した場合は <see langword="true"/>、それ以外は <see langword="false"/>。</returns>
42 対象外 public static bool TryParse(string? pattern, out ParallelDiffPathPattern? parsedPattern)
43 対象外 {
44 実行済み 44 parsedPattern = null;
45 実行済み 44 if (string.IsNullOrEmpty(pattern)
46 実行済み 44 || !TrySplitSegments(pattern, out var rawSegments))
47 対象外 {
48 実行済み 6 return false;
49 対象外 }
50 対象外
51 実行済み 38 var segments = new List<PatternSegment>(rawSegments.Count);
52 実行済み 247 foreach (var rawSegment in rawSegments)
53 対象外 {
54 実行済み 87 if (!TryParsePatternSegment(rawSegment, out var segment))
55 対象外 {
56 実行済み 3 return false;
57 対象外 }
58 対象外
59 実行済み 84 segments.Add(segment);
60 対象外 }
61 対象外
62 実行済み 35 parsedPattern = new ParallelDiffPathPattern(segments);
63 実行済み 35 return true;
64 実行済み 3 }
65 対象外
66 対象外 /// <summary>
67 対象外 /// 指定した XPath-like path 自身、またはその祖先がこの pattern に一致するか判定します。
68 対象外 /// </summary>
69 対象外 /// <remarks>
70 対象外 /// 空文字列の比較 key に対して <c>GetDiffEntries()</c> が生成する legacy 空 selector は、候補 path に限り空 key selector として照合します。
71 対象外 /// </remarks>
72 対象外 /// <param name="path">照合する <see cref="ParallelDiffEntry.Path"/>。</param>
73 対象外 /// <returns>pattern の全 segment が path の先頭から一致する場合は <see langword="true"/>。</returns>
74 対象外 /// <exception cref="ArgumentNullException"><paramref name="path"/> が <see langword="null"/> の場合。</exception>
75 対象外 public bool IsMatch(string path)
76 対象外 {
77 実行済み 61 ArgumentNullException.ThrowIfNull(path);
78 対象外
79 実行済み 60 if (!XPathLikePathParser.TryParseLegacyEmptyKeySelectorPath(path, out var parsedPath)
80 実行済み 60 || parsedPath is null
81 実行済み 60 || parsedPath.Segments.Count < _segments.Count)
82 対象外 {
83 実行済み 2 return false;
84 対象外 }
85 対象外
86 実行済み 314 for (var index = 0; index < _segments.Count; index++)
87 対象外 {
88 実行済み 124 if (!_segments[index].IsMatch(parsedPath.Segments[index]))
89 対象外 {
90 実行済み 25 return false;
91 対象外 }
92 対象外 }
93 対象外
94 実行済み 33 return true;
95 対象外 }
96 対象外
97 対象外 /// <summary>
98 対象外 /// 一つの pattern segment を既存 path grammar または selector wildcard grammar として解析します。
99 対象外 /// </summary>
100 対象外 /// <remarks>
101 対象外 /// <c>[*]</c> は任意 selector を表し、<c>[\*]</c> は <c>*</c> をエスケープして通常文字の key として扱います。
102 対象外 /// </remarks>
103 対象外 private static bool TryParsePatternSegment(string rawSegment, out PatternSegment segment)
104 対象外 {
105 実行済み 87 segment = null!;
106 実行済み 87 var selectorStart = rawSegment.IndexOf('[', StringComparison.Ordinal);
107 実行済み 87 if (selectorStart < 0)
108 対象外 {
109 実行済み 49 if (!XPathLikePathParser.TryParse(rawSegment, out var parsedPath)
110 実行済み 49 || parsedPath is null
111 実行済み 49 || parsedPath.Segments.Count != 1)
112 対象外 {
113 未実行 0 return false;
114 対象外 }
115 対象外
116 実行済み 49 var parsedSegment = parsedPath.Segments[0];
117 実行済み 49 if (parsedSegment.Selector is not null)
118 対象外 {
119 未実行 0 return false;
120 対象外 }
121 対象外
122 実行済み 49 segment = new PatternSegment(parsedSegment.MemberName, selector: null);
123 実行済み 49 return true;
124 対象外 }
125 対象外
126 実行済み 38 if (selectorStart == 0 || !rawSegment.EndsWith(']'))
127 対象外 {
128 未実行 0 return false;
129 対象外 }
130 対象外
131 実行済み 38 var selectorText = rawSegment[(selectorStart + 1)..^1];
132 実行済み 38 if (selectorText == "*")
133 対象外 {
134 実行済み 23 var memberName = rawSegment[..selectorStart];
135 実行済み 23 if (memberName.Length == 0
136 実行済み 23 || memberName.Contains('[', StringComparison.Ordinal)
137 実行済み 23 || memberName.Contains(']', StringComparison.Ordinal))
138 対象外 {
139 未実行 0 return false;
140 対象外 }
141 対象外
142 実行済み 23 segment = new PatternSegment(memberName, PatternSelector.Any);
143 実行済み 23 return true;
144 対象外 }
145 対象外
146 実行済み 15 var exactSegmentText = selectorText == "\\*"
147 実行済み 15 ? string.Concat(rawSegment.AsSpan(0, selectorStart + 1), "*]")
148 実行済み 15 : rawSegment;
149 実行済み 15 if (!XPathLikePathParser.TryParse(exactSegmentText, out var exactPath)
150 実行済み 15 || exactPath is null
151 実行済み 15 || exactPath.Segments.Count != 1)
152 対象外 {
153 実行済み 3 return false;
154 対象外 }
155 対象外
156 実行済み 12 var exactSegment = exactPath.Segments[0];
157 実行済み 12 if (exactSegment.Selector is null)
158 対象外 {
159 未実行 0 return false;
160 対象外 }
161 対象外
162 実行済み 12 segment = new PatternSegment(
163 実行済み 12 exactSegment.MemberName,
164 実行済み 12 PatternSelector.Exact(exactSegment.Selector));
165 実行済み 12 return true;
166 対象外 }
167 対象外
168 対象外 /// <summary>
169 対象外 /// selector 内の escape を考慮して root-relative pattern を segment 文字列へ分割します。
170 対象外 /// </summary>
171 対象外 /// <remarks>
172 対象外 /// selector が閉じた後の追加 selector や未閉じ selector を拒否し、既存 XPath-like path の境界規則を維持します。
173 対象外 /// </remarks>
174 対象外 private static bool TrySplitSegments(string pattern, out List<string> segments)
175 対象外 {
176 実行済み 42 segments = [];
177 実行済み 42 var segmentStart = 0;
178 実行済み 42 var inSelector = false;
179 実行済み 42 var escaping = false;
180 実行済み 42 var selectorClosedInSegment = false;
181 対象外
182 実行済み 1464 for (var index = 0; index < pattern.Length; index++)
183 対象外 {
184 実行済み 692 var current = pattern[index];
185 実行済み 692 if (inSelector)
186 対象外 {
187 実行済み 146 if (escaping)
188 対象外 {
189 実行済み 6 escaping = false;
190 実行済み 6 continue;
191 対象外 }
192 対象外
193 実行済み 140 if (current == '\\')
194 対象外 {
195 実行済み 6 escaping = true;
196 実行済み 6 continue;
197 対象外 }
198 対象外
199 実行済み 134 if (current == ']')
200 対象外 {
201 実行済み 40 inSelector = false;
202 実行済み 40 selectorClosedInSegment = true;
203 対象外 }
204 対象外
205 実行済み 40 continue;
206 対象外 }
207 対象外
208 実行済み 546 if (current == '[')
209 対象外 {
210 実行済み 42 if (selectorClosedInSegment)
211 対象外 {
212 実行済み 1 return false;
213 対象外 }
214 対象外
215 実行済み 41 inSelector = true;
216 実行済み 41 continue;
217 対象外 }
218 対象外
219 実行済み 504 if (current == ']')
220 対象外 {
221 未実行 0 return false;
222 対象外 }
223 対象外
224 実行済み 504 if (current != '.')
225 対象外 {
226 対象外 continue;
227 対象外 }
228 対象外
229 実行済み 51 if (index == segmentStart)
230 対象外 {
231 実行済み 1 return false;
232 対象外 }
233 対象外
234 実行済み 50 segments.Add(pattern[segmentStart..index]);
235 実行済み 50 segmentStart = index + 1;
236 実行済み 50 selectorClosedInSegment = false;
237 対象外 }
238 対象外
239 実行済み 40 if (inSelector || escaping || segmentStart == pattern.Length)
240 対象外 {
241 実行済み 2 return false;
242 対象外 }
243 対象外
244 実行済み 38 segments.Add(pattern[segmentStart..]);
245 実行済み 38 return true;
246 対象外 }
247 対象外
248 対象外 private sealed class PatternSegment
249 対象外 {
250 実行済み 84 public PatternSegment(string memberName, PatternSelector? selector)
251 対象外 {
252 実行済み 84 MemberName = memberName;
253 実行済み 84 Selector = selector;
254 実行済み 84 }
255 対象外
256 実行済み 124 public string MemberName { get; }
257 対象外
258 実行済み 164 public PatternSelector? Selector { get; }
259 対象外
260 対象外 /// <summary>
261 対象外 /// member 名と selector の両方を比較して、候補 segment がこの pattern segment に一致するか判定します。
262 対象外 /// </summary>
263 対象外 public bool IsMatch(XPathLikePathSegment candidate)
264 対象外 {
265 実行済み 124 if (!string.Equals(MemberName, candidate.MemberName, StringComparison.Ordinal))
266 対象外 {
267 実行済み 13 return false;
268 対象外 }
269 対象外
270 実行済み 111 if (Selector is null)
271 対象外 {
272 実行済み 56 return candidate.Selector is null;
273 対象外 }
274 対象外
275 実行済み 55 return candidate.Selector is not null && Selector.IsMatch(candidate.Selector);
276 対象外 }
277 対象外 }
278 対象外
279 対象外 private sealed class PatternSelector
280 対象外 {
281 実行済み 14 private PatternSelector(bool matchesAny, XPathLikePathSelector? exactSelector)
282 対象外 {
283 実行済み 14 MatchesAny = matchesAny;
284 実行済み 14 ExactSelector = exactSelector;
285 実行済み 14 }
286 対象外
287 実行済み 25 public static PatternSelector Any { get; } = new(matchesAny: true, exactSelector: null);
288 対象外
289 実行済み 53 public bool MatchesAny { get; }
290 対象外
291 実行済み 86 public XPathLikePathSelector? ExactSelector { get; }
292 対象外
293 対象外 /// <summary>
294 対象外 /// 指定した selector を保持し、wildcard を使わない exact selector pattern を生成します。
295 対象外 /// </summary>
296 対象外 /// <param name="selector">生成した pattern が照合時に比較する selector。</param>
297 対象外 /// <returns>指定した <paramref name="selector"/> を exact selector として保持する pattern。</returns>
298 対象外 public static PatternSelector Exact(XPathLikePathSelector selector)
299 対象外 {
300 実行済み 12 return new PatternSelector(matchesAny: false, selector);
301 対象外 }
302 対象外
303 対象外 /// <summary>
304 対象外 /// wildcard または exact selector の規則で候補 selector が一致するか判定します。
305 対象外 /// </summary>
306 対象外 public bool IsMatch(XPathLikePathSelector candidate)
307 対象外 {
308 実行済み 53 if (MatchesAny)
309 対象外 {
310 実行済み 30 return true;
311 対象外 }
312 対象外
313 実行済み 23 if (ExactSelector is null || ExactSelector.Kind != candidate.Kind)
314 対象外 {
315 実行済み 3 return false;
316 対象外 }
317 対象外
318 実行済み 20 return ExactSelector.Kind switch
319 実行済み 20 {
320 実行済み 19 XPathLikePathSelectorKind.Key => string.Equals(
321 実行済み 19 ExactSelector.KeyText,
322 実行済み 19 candidate.KeyText,
323 実行済み 19 StringComparison.Ordinal),
324 実行済み 1 XPathLikePathSelectorKind.Ordinal => ExactSelector.Ordinal == candidate.Ordinal,
325 未実行 0 _ => false,
326 実行済み 20 };
327 対象外 }
328 対象外 }
329 対象外 }
330 対象外
331 対象外 /// <summary>
332 対象外 /// <see cref="ParallelDiffEntry"/> の path pattern 判定を提供します。
333 対象外 /// </summary>
334 対象外 public static class ParallelDiffEntryPathExtensions
335 対象外 {
336 対象外 /// <summary>
337 対象外 /// 差分 entry の path 自身、またはその祖先が指定 pattern に一致するか判定します。
338 対象外 /// </summary>
339 対象外 /// <param name="entry">判定する差分 entry。</param>
340 対象外 /// <param name="pattern">照合する path pattern。</param>
341 対象外 /// <returns>pattern が差分 path 自身または祖先に一致する場合は <see langword="true"/>。</returns>
342 対象外 /// <exception cref="ArgumentNullException"><paramref name="entry"/> または <paramref name="pattern"/> が <see langword="null"/> の場合。</exception>
343 対象外 public static bool PathMatches(
344 対象外 this ParallelDiffEntry entry,
345 対象外 ParallelDiffPathPattern pattern)
346 対象外 {
347 実行済み 14 ArgumentNullException.ThrowIfNull(entry);
348 実行済み 13 ArgumentNullException.ThrowIfNull(pattern);
349 対象外
350 実行済み 12 return pattern.IsMatch(entry.Path);
351 対象外 }
352 対象外 }