You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
6.3 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace IPA.ArgParsing
  9. {
  10. public class Arguments
  11. {
  12. public static Arguments CmdLine = new Arguments(Environment.GetCommandLineArgs());
  13. private List<string> positional = new List<string>();
  14. private Dictionary<string, string> longFlags = new Dictionary<string, string>();
  15. private Dictionary<char, string> flags = new Dictionary<char, string>();
  16. private List<ArgumentFlag> flagObjects = new List<ArgumentFlag>();
  17. private string[] toParse = null;
  18. private Arguments(string[] args)
  19. {
  20. toParse = args.Skip(1).ToArray();
  21. }
  22. public Arguments Flags(params ArgumentFlag[] toAdd)
  23. {
  24. foreach (var f in toAdd) AddFlag(f);
  25. return this;
  26. }
  27. public void AddFlag(ArgumentFlag toAdd)
  28. {
  29. if (toParse == null) throw new InvalidOperationException();
  30. flagObjects.Add(toAdd);
  31. }
  32. public void Process()
  33. {
  34. foreach (var arg in toParse)
  35. {
  36. if (arg.StartsWith("--"))
  37. { // parse as a long flag
  38. var name = arg.Substring(2); // cut off first two chars
  39. string value = null;
  40. if (name.Contains('='))
  41. {
  42. var spl = name.Split('=');
  43. name = spl[0];
  44. value = string.Join("=", spl, 1, spl.Length - 1);
  45. }
  46. longFlags.Add(name, value);
  47. }
  48. else if (arg.StartsWith("-"))
  49. { // parse as flags
  50. var argument = arg.Substring(1); // cut off first char
  51. StringBuilder subBuildState = new StringBuilder();
  52. bool parsingValue = false;
  53. char mainChar = ' ';
  54. foreach (char chr in argument)
  55. {
  56. if (!parsingValue)
  57. {
  58. if (chr == '=')
  59. {
  60. parsingValue = true;
  61. }
  62. else
  63. {
  64. mainChar = chr;
  65. flags.Add(chr, null);
  66. }
  67. }
  68. else
  69. {
  70. if (chr == ',')
  71. {
  72. parsingValue = false;
  73. flags[mainChar] = subBuildState.ToString();
  74. subBuildState = new StringBuilder();
  75. }
  76. else
  77. {
  78. subBuildState.Append(chr);
  79. }
  80. }
  81. }
  82. }
  83. else
  84. { // parse as positional
  85. positional.Add(arg);
  86. }
  87. }
  88. toParse = null;
  89. foreach (var flag in flagObjects)
  90. {
  91. foreach (var chflag in flag.shortFlags)
  92. {
  93. if (flag.exists = HasFlag(chflag))
  94. {
  95. flag.value = GetFlagValue(chflag);
  96. goto FoundValue; // continue to next flagObjects item
  97. }
  98. }
  99. foreach (var lflag in flag.longFlags)
  100. {
  101. if (flag.exists = HasLongFlag(lflag))
  102. {
  103. flag.value = GetLongFlagValue(lflag);
  104. goto FoundValue; // continue to next flagObjects item
  105. }
  106. }
  107. FoundValue:;
  108. }
  109. }
  110. public bool HasLongFlag(string flag)
  111. {
  112. return longFlags.ContainsKey(flag);
  113. }
  114. public bool HasFlag(char flag)
  115. {
  116. return flags.ContainsKey(flag);
  117. }
  118. public string GetLongFlagValue(string flag)
  119. {
  120. return longFlags[flag];
  121. }
  122. public string GetFlagValue(char flag)
  123. {
  124. return flags[flag];
  125. }
  126. public void PrintHelp()
  127. {
  128. const string indent = " ";
  129. string filename = Environment.GetCommandLineArgs()[0];
  130. string format = @"usage:
  131. {2}{0} [FLAGS] [ARGUMENTS]
  132. flags:
  133. {1}";
  134. var flagsBuilder = new StringBuilder();
  135. foreach (var flag in flagObjects)
  136. {
  137. flagsBuilder.AppendFormat("{2}{0}{3}{1}",
  138. string.Join(", ", flag.shortFlags.Select(s => $"-{s}").Concat( flag.longFlags.Select(s => $"--{s}")) ),
  139. Environment.NewLine, indent, flag.ValueString != null ? "=" + flag.ValueString : "");
  140. flagsBuilder.AppendFormat("{2}{2}{0}{1}", flag.DocString, Environment.NewLine, indent);
  141. }
  142. Console.Write(string.Format(format, filename, flagsBuilder.ToString(), indent));
  143. }
  144. public IReadOnlyList<string> PositionalArgs => positional;
  145. }
  146. public class ArgumentFlag
  147. {
  148. internal List<char> shortFlags = new List<char>();
  149. internal List<string> longFlags = new List<string>();
  150. internal string value = null;
  151. internal bool exists = false;
  152. public ArgumentFlag(params string[] flags)
  153. {
  154. foreach (var part in flags)
  155. AddPart(part);
  156. }
  157. private void AddPart(string flagPart)
  158. {
  159. if (flagPart.StartsWith("--"))
  160. longFlags.Add(flagPart.Substring(2));
  161. else if (flagPart.StartsWith("-"))
  162. shortFlags.Add(flagPart[1]);
  163. }
  164. public bool Exists => exists;
  165. public string Value => value;
  166. public bool HasValue => Exists && Value != null;
  167. public string DocString { get; set; } = "";
  168. public string ValueString { get; set; } = null;
  169. public static implicit operator bool(ArgumentFlag f)
  170. {
  171. return f.Exists;
  172. }
  173. }
  174. }