Skip to content

Commit c4861b4

Browse files
committed
正确处理所有命令行风格中的列表值的解析
1 parent 7559ba0 commit c4861b4

File tree

9 files changed

+811
-45
lines changed

9 files changed

+811
-45
lines changed

src/DotNetCampus.CommandLine.Analyzer/Generators/BuilderGenerator.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,7 @@ private string GenerateValuePropertyAssignment(CommandObjectGeneratingModel mode
194194
{
195195
return targetType.Name switch
196196
{
197-
"IEnumerable" or "IReadOnlyList" => "AsReadOnlyList",
198-
"IList" or "ICollection" => "ToList",
197+
"IEnumerable" or "IReadOnlyList" or "IList" or "ICollection" => "ToList",
199198
"IReadOnlyDictionary" or "IDictionary" => "ToDictionary",
200199
// 专门生成不存在的方法名和全名注释,编译不通过,同时还能辅助报告错误原因。
201200
_ => $"To{targetType.Name}/* {targetType.ToDisplayString()} */",

src/DotNetCampus.CommandLine/CommandLinePropertyValue.cs

Lines changed: 235 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -248,26 +248,26 @@ public static implicit operator bool(CommandLinePropertyValue propertyValue)
248248
/// </summary>
249249
public static implicit operator string[](CommandLinePropertyValue propertyValue) => propertyValue._values switch
250250
{
251-
{ Count: 0 } => Array.Empty<string>(),
252-
{ } values => values.ToArray(),
251+
{ Count: 0 } => [],
252+
{ } values => [..SplitValues(values)],
253253
};
254254

255255
/// <summary>
256256
/// 将从命令行解析出来的属性值转换为不可变字符串数组。
257257
/// </summary>
258258
public static implicit operator ImmutableArray<string>(CommandLinePropertyValue propertyValue) => propertyValue._values switch
259259
{
260-
{ Count: 0 } => ImmutableArray<string>.Empty,
261-
{ } values => values.ToImmutableArray(),
260+
{ Count: 0 } => [],
261+
{ } values => [..SplitValues(values)],
262262
};
263263

264264
/// <summary>
265265
/// 将从命令行解析出来的属性值转换为不可变字符串哈希集合。
266266
/// </summary>
267267
public static implicit operator ImmutableHashSet<string>(CommandLinePropertyValue propertyValue) => propertyValue._values switch
268268
{
269-
{ Count: 0 } => ImmutableHashSet<string>.Empty,
270-
{ } values => values.ToImmutableHashSet(),
269+
{ Count: 0 } => [],
270+
{ } values => [..SplitValues(values)],
271271
};
272272

273273
/// <summary>
@@ -276,17 +276,13 @@ public static implicit operator bool(CommandLinePropertyValue propertyValue)
276276
public static implicit operator Collection<string>(CommandLinePropertyValue propertyValue) => propertyValue._values switch
277277
{
278278
{ Count: 0 } => [],
279-
{ } values => [..values],
279+
{ } values => [..SplitValues(values)],
280280
};
281281

282282
/// <summary>
283283
/// 将从命令行解析出来的属性值转换为字符串列表。
284284
/// </summary>
285-
public static implicit operator List<string>(CommandLinePropertyValue propertyValue) => propertyValue._values switch
286-
{
287-
{ Count: 0 } => [],
288-
{ } values => values.ToList(),
289-
};
285+
public static implicit operator List<string>(CommandLinePropertyValue propertyValue) => propertyValue.ToList();
290286

291287
/// <summary>
292288
/// 将从命令行解析出来的属性值转换为字符串键值对。
@@ -298,11 +294,6 @@ public static implicit operator bool(CommandLinePropertyValue propertyValue)
298294
/// </summary>
299295
public static implicit operator Dictionary<string, string>(CommandLinePropertyValue propertyValue) => propertyValue.ToDictionary();
300296

301-
/// <summary>
302-
/// 将从命令行解析出来的属性值以只读列表的形式访问。
303-
/// </summary>
304-
public IReadOnlyList<string> AsReadOnlyList() => _values;
305-
306297
/// <summary>
307298
/// 将从命令行解析出来的属性值转换为枚举值。
308299
/// </summary>
@@ -318,7 +309,7 @@ public static implicit operator bool(CommandLinePropertyValue propertyValue)
318309
public List<string> ToList() => _values switch
319310
{
320311
{ Count: 0 } => [],
321-
{ } values => values.ToList(),
312+
{ } values => [..SplitValues(values)],
322313
};
323314

324315
/// <summary>
@@ -342,6 +333,232 @@ public static implicit operator bool(CommandLinePropertyValue propertyValue)
342333
.GroupBy(x => x.Key)
343334
.ToDictionary(x => x.Key, x => x.Last().Value),
344335
};
336+
337+
private static IEnumerable<string> SplitValues(IReadOnlyList<string> commandLineValues)
338+
{
339+
for (var commandLineValueIndex = 0; commandLineValueIndex < commandLineValues.Count; commandLineValueIndex++)
340+
{
341+
var optionValue = commandLineValues[commandLineValueIndex];
342+
var lastPart = ListValueParsingType.Start;
343+
var thisPartStartIndex = 0;
344+
for (var index = 0; index < optionValue.Length; index++)
345+
{
346+
var c = optionValue[index];
347+
348+
// 引号
349+
if (c is '"')
350+
{
351+
if (lastPart is ListValueParsingType.Start)
352+
{
353+
// 开始的引号
354+
lastPart = ListValueParsingType.QuoteStart;
355+
continue;
356+
}
357+
if (lastPart is ListValueParsingType.QuoteStart)
358+
{
359+
// 连续出现的引号
360+
yield return "";
361+
lastPart = ListValueParsingType.QuoteEnd;
362+
continue;
363+
}
364+
if (lastPart is ListValueParsingType.QuotedValue)
365+
{
366+
// 引号中值后的引号
367+
yield return optionValue[thisPartStartIndex..index];
368+
lastPart = ListValueParsingType.QuoteEnd;
369+
continue;
370+
}
371+
if (lastPart is ListValueParsingType.QuotedSeparator)
372+
{
373+
// 引号中分割符后的引号
374+
yield return "";
375+
lastPart = ListValueParsingType.QuotedValue;
376+
continue;
377+
}
378+
if (lastPart is ListValueParsingType.QuoteEnd)
379+
{
380+
// 引号结束后的引号
381+
lastPart = ListValueParsingType.QuoteStart;
382+
continue;
383+
}
384+
if (lastPart is ListValueParsingType.Value)
385+
{
386+
// 正常值后的引号
387+
throw new CommandLineParseValueException(
388+
$"Invalid value format at index [{index}]: {optionValue}");
389+
}
390+
if (lastPart is ListValueParsingType.Separator)
391+
{
392+
// 正常分隔符后的引号
393+
lastPart = ListValueParsingType.QuoteStart;
394+
continue;
395+
}
396+
}
397+
398+
// 分割符
399+
if (c is ';' or ',')
400+
{
401+
if (lastPart is ListValueParsingType.Start)
402+
{
403+
// 开始的分割符
404+
yield return "";
405+
lastPart = ListValueParsingType.Separator;
406+
continue;
407+
}
408+
if (lastPart is ListValueParsingType.QuoteStart)
409+
{
410+
// 引号后紧跟着的分割符(等同于正常字符)
411+
lastPart = ListValueParsingType.QuotedValue;
412+
continue;
413+
}
414+
if (lastPart is ListValueParsingType.QuotedValue)
415+
{
416+
// 引号中值后的分割符(等同于正常字符)
417+
lastPart = ListValueParsingType.QuotedValue;
418+
continue;
419+
}
420+
if (lastPart is ListValueParsingType.QuotedSeparator)
421+
{
422+
// 引号中连续出现的分割符(等同于正常字符)
423+
lastPart = ListValueParsingType.QuotedValue;
424+
continue;
425+
}
426+
if (lastPart is ListValueParsingType.QuoteEnd)
427+
{
428+
// 引号结束后的分割符
429+
lastPart = ListValueParsingType.Separator;
430+
continue;
431+
}
432+
if (lastPart is ListValueParsingType.Value)
433+
{
434+
// 正常值后的分割符
435+
yield return optionValue[thisPartStartIndex..index];
436+
lastPart = ListValueParsingType.Separator;
437+
continue;
438+
}
439+
if (lastPart is ListValueParsingType.Separator)
440+
{
441+
// 连续出现的分割符
442+
yield return "";
443+
lastPart = ListValueParsingType.Separator;
444+
continue;
445+
}
446+
}
447+
448+
// 其他字符
449+
if (lastPart is ListValueParsingType.Start)
450+
{
451+
// 开始的值
452+
thisPartStartIndex = index;
453+
lastPart = ListValueParsingType.Value;
454+
continue;
455+
}
456+
if (lastPart is ListValueParsingType.QuoteStart)
457+
{
458+
// 引号后紧跟着的值
459+
thisPartStartIndex = index;
460+
lastPart = ListValueParsingType.QuotedValue;
461+
continue;
462+
}
463+
if (lastPart is ListValueParsingType.QuotedValue)
464+
{
465+
// 引号中值后的值
466+
lastPart = ListValueParsingType.QuotedValue;
467+
continue;
468+
}
469+
if (lastPart is ListValueParsingType.QuotedSeparator)
470+
{
471+
// 引号中分割符(实际上就是正常值)后的值
472+
lastPart = ListValueParsingType.QuotedValue;
473+
continue;
474+
}
475+
if (lastPart is ListValueParsingType.QuoteEnd)
476+
{
477+
// 引号结束后的值
478+
throw new CommandLineParseValueException(
479+
$"Invalid value format at index [{index}]: {optionValue}");
480+
}
481+
if (lastPart is ListValueParsingType.Value)
482+
{
483+
// 正常值后的值
484+
lastPart = ListValueParsingType.Value;
485+
continue;
486+
}
487+
if (lastPart is ListValueParsingType.Separator)
488+
{
489+
// 正常分割符后的值
490+
thisPartStartIndex = index;
491+
lastPart = ListValueParsingType.Value;
492+
continue;
493+
}
494+
}
495+
496+
// 处理最后一个值
497+
if (lastPart is ListValueParsingType.Start)
498+
{
499+
// 一开始就结束了(字符串里就没有值)
500+
yield return "";
501+
}
502+
else if (lastPart is ListValueParsingType.QuoteStart or ListValueParsingType.QuotedValue or ListValueParsingType.QuotedSeparator)
503+
{
504+
// 引号还没结束,字符串就结束了
505+
throw new CommandLineParseValueException(
506+
$"Missing quote end at index [{optionValue.Length}]: {optionValue}");
507+
}
508+
else if (lastPart is ListValueParsingType.QuoteEnd)
509+
{
510+
// 引号结束后字符串正常结束
511+
}
512+
else if (lastPart is ListValueParsingType.Value)
513+
{
514+
// 正常值结束的字符串
515+
yield return optionValue[thisPartStartIndex..];
516+
}
517+
else if (lastPart is ListValueParsingType.Separator)
518+
{
519+
// 正常分割符后就结束了字符串
520+
yield return "";
521+
}
522+
}
523+
}
524+
}
525+
526+
file enum ListValueParsingType
527+
{
528+
/// <summary>
529+
/// 尚未开始分割。
530+
/// </summary>
531+
Start,
532+
533+
/// <summary>
534+
/// 引号开始。
535+
/// </summary>
536+
QuoteStart,
537+
538+
/// <summary>
539+
/// 引号中的值。
540+
/// </summary>
541+
QuotedValue,
542+
543+
/// <summary>
544+
/// 引号中的分割符。
545+
/// </summary>
546+
QuotedSeparator,
547+
548+
/// <summary>
549+
/// 引号结束。
550+
/// </summary>
551+
QuoteEnd,
552+
553+
/// <summary>
554+
/// 正常值。
555+
/// </summary>
556+
Value,
557+
558+
/// <summary>
559+
/// 正常分割符。
560+
/// </summary>
561+
Separator,
345562
}
346563

347564
internal enum MultiValueHandling

0 commit comments

Comments
 (0)