├── src ├── Testee │ ├── paket.references │ ├── App.config │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Program.cs │ └── Testee.csproj ├── DocoptNet.Tests │ ├── paket.references │ ├── app.config │ ├── DocoptNet.Tests.csproj.DotSettings │ ├── OptionNameTests.cs │ ├── Assumptions.cs │ ├── StringPartitionTests.cs │ ├── PatternFlatTests.cs │ ├── DocoptNet.Tests.csproj │ ├── ParseDefaultsTests.cs │ ├── PatternFixIdentitiesTests.cs │ ├── ShortOptionsErrorHandlingTests.cs │ ├── AnyOptionsParameterTests.cs │ ├── PatternFixRepeatingArgumentsTests.cs │ ├── GenerateCodeHelperTest.cs │ ├── DefaultValueForPositionalArgumentsTests.cs │ ├── OptionsFirstTests.cs │ ├── RequiredMatchTests.cs │ ├── ArgumentMatchTests.cs │ ├── CommandMatchTests.cs │ ├── UsageTests.cs │ ├── LongOptionsErrorHandlingTests.cs │ ├── ListArgumentMatchTests.cs │ ├── BasicPatternMatchingTests.cs │ ├── OptionMatchTests.cs │ ├── LanguageAgnosticTests.cs │ ├── EitherMatchTests.cs │ ├── OptionParseTests.cs │ ├── PatterEitherTests.cs │ ├── GenerateCodeTests.cs │ ├── CommandsTests.cs │ ├── SyntaxTests.cs │ ├── CountMultipleFlagsTests.cs │ ├── OptionalMatchTests.cs │ ├── ParseArgvTests.cs │ ├── GetNodesTests.cs │ ├── OneOrMoreMatchTests.cs │ ├── ParsePatternTests.cs │ ├── DocoptTests.cs │ └── ValueObjectTests.cs ├── DocoptNet │ ├── DocoptNet.snk │ ├── DocoptNet.publickey │ ├── OptionsShortcut.cs │ ├── GenerateCodeHelper.cs │ ├── Optional.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Required.cs │ ├── Either.cs │ ├── DocoptExitException.cs │ ├── DocoptLanguageErrorException.cs │ ├── DocoptInputErrorException.cs │ ├── Command.cs │ ├── DocoptBaseException.cs │ ├── BranchPattern.cs │ ├── OneOrMore.cs │ ├── StringPartition.cs │ ├── MatchResult.cs │ ├── Argument.cs │ ├── Tokens.cs │ ├── Node.cs │ ├── LeafPattern.cs │ ├── Option.cs │ ├── DocoptNet.csproj │ ├── ValueObject.cs │ └── Pattern.cs ├── Examples │ └── NavalFate │ │ ├── App.config │ │ ├── Program.cs │ │ ├── Properties │ │ └── AssemblyInfo.cs │ │ └── NavalFate.csproj ├── T4DocoptNetHostApp │ ├── Another.usage.txt │ ├── App.config │ ├── Program.cs │ ├── Main.usage.txt │ ├── T4DocoptNet.tt.hooks.t4 │ ├── T4DocoptNet.tt.settings.xml │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── T4DocoptNet.cs │ ├── T4DocoptNet.tt │ └── T4DocoptNetHostApp.csproj ├── LanguageAgnosticTests │ ├── README.md │ ├── generate_tests.py │ ├── language_agnostic_tester.py │ └── testcases.docopt ├── NuGet │ ├── Main.usage.txt │ └── install.ps1 └── DocoptNet.sln ├── .paket ├── paket.bootstrapper.exe └── paket.targets ├── paket.dependencies ├── appveyor.yml ├── .gitignore ├── paket.lock ├── .gitattributes └── LICENSE-MIT /src/Testee/paket.references: -------------------------------------------------------------------------------- 1 | Newtonsoft.Json -------------------------------------------------------------------------------- /src/DocoptNet.Tests/paket.references: -------------------------------------------------------------------------------- 1 | Newtonsoft.Json 2 | NSubstitute 3 | NUnit -------------------------------------------------------------------------------- /src/DocoptNet/DocoptNet.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/docopt.net/master/src/DocoptNet/DocoptNet.snk -------------------------------------------------------------------------------- /.paket/paket.bootstrapper.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/docopt.net/master/.paket/paket.bootstrapper.exe -------------------------------------------------------------------------------- /src/DocoptNet/DocoptNet.publickey: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/baruchiro/docopt.net/master/src/DocoptNet/DocoptNet.publickey -------------------------------------------------------------------------------- /src/DocoptNet.Tests/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/Testee/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/Examples/NavalFate/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/Another.usage.txt: -------------------------------------------------------------------------------- 1 | Test host app for T4 Docopt.NET 2 | 3 | Usage: 4 | other command1 ARG [-o --long=ARG --switch -v] 5 | other command2 6 | 7 | Options: 8 | -o A string with some double "quotes". 9 | -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /paket.dependencies: -------------------------------------------------------------------------------- 1 | source https://www.nuget.org/api/v2/ 2 | 3 | nuget NuGet.CommandLine 4 | nuget Newtonsoft.Json 5 | nuget NSubstitute 6 | nuget NUnit 7 | 8 | group Build 9 | source https://www.nuget.org/api/v2/ 10 | nuget NUnit.Runners 11 | nuget MSBuildTasks -------------------------------------------------------------------------------- /src/DocoptNet/OptionsShortcut.cs: -------------------------------------------------------------------------------- 1 | namespace DocoptNet 2 | { 3 | /// 4 | /// Marker/placeholder for [options] shortcut. 5 | /// 6 | internal class OptionsShortcut : Optional 7 | { 8 | public OptionsShortcut() : base(new Pattern[0]) 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace T4DocoptNetHostApp 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | var mainArgs = new MainArgs(args); 10 | if (mainArgs.CmdCommand) 11 | Console.WriteLine("First command"); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.1.{build} 2 | image: Visual Studio 2017 Preview 3 | configuration: Release 4 | build_script: 5 | - cmd: dotnet build src\DocoptNet.sln --configuration Release 6 | - cmd: IF NOT EXIST dist MKDIR dist 7 | - cmd: dotnet pack --configuration Release src\DocoptNet 8 | test_script: 9 | - cmd: dotnet test src\DocoptNet.Tests 10 | artifacts: 11 | - path: '**\docopt.net*.nupkg' 12 | name: Nuget 13 | -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/Main.usage.txt: -------------------------------------------------------------------------------- 1 | Test host app for T4 Docopt.NET 2 | 3 | Usage: 4 | prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch] 5 | prog files FILE... 6 | 7 | Options: 8 | -o Short switch. 9 | -s= Short option with arg. 10 | --long=ARG Long option with arg. 11 | --switch Long switch. 12 | 13 | Explanation: 14 | This is a test usage file. 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Thumbs.db 2 | *.obj 3 | *.exe 4 | *.pdb 5 | *.user 6 | *.aps 7 | *.pch 8 | *.vspscc 9 | *_i.c 10 | *_p.c 11 | *.ncb 12 | *.suo 13 | *.sln.docstates 14 | *.tlb 15 | *.tlh 16 | *.bak 17 | *.cache 18 | *.ilk 19 | *.log 20 | [Bb]in 21 | [Dd]ebug*/ 22 | *.lib 23 | *.sbr 24 | obj/ 25 | [Rr]elease*/ 26 | _ReSharper*/ 27 | [Tt]est[Rr]esult* 28 | packages/ 29 | *.nupkg 30 | /_build 31 | .vs/ 32 | .paket/Paket.Restore.targets 33 | -------------------------------------------------------------------------------- /paket.lock: -------------------------------------------------------------------------------- 1 | NUGET 2 | remote: https://www.nuget.org/api/v2 3 | specs: 4 | FSharp.TypeProviders.StarterPack (1.1.3.56) - framework: >= net35 5 | Newtonsoft.Json (5.0.5) 6 | NSubstitute (1.6.1) - framework: >= net40 7 | NuGet.CommandLine (3.3) 8 | NUnit (2.6.2) - framework: >= net40 9 | 10 | GROUP Build 11 | NUGET 12 | remote: https://www.nuget.org/api/v2 13 | specs: 14 | MSBuildTasks (1.4.0.61) 15 | NUnit.Runners (2.6.2) 16 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/DocoptNet.Tests.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | CSharp40 -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/T4DocoptNet.tt.hooks.t4: -------------------------------------------------------------------------------- 1 | <#+ 2 | /* 3 | 4 | This file contains hooks and extra code used by T4DocoptNet.tt. The main goal is to avoid the need for users 5 | to fork the 'official' template in order to achieve what they want. 6 | 7 | */ 8 | 9 | void RenderAdditionalCode() { 10 | #> 11 | [GeneratedCode("T4DocoptNet", "2.0"), DebuggerNonUserCode] 12 | internal static class T4DocoptNetHelpers { 13 | // TODO 14 | } 15 | 16 | <#+ 17 | } 18 | #> 19 | -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/T4DocoptNet.tt.settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | T4DocoptNet 5 | 6 | Args 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/LanguageAgnosticTests/README.md: -------------------------------------------------------------------------------- 1 | Language Agnostic Tests 2 | ======================= 3 | 4 | Method 1: testee app 5 | -------------------- 6 | 1. Compile the Testee project 7 | 2. Run the following commands 8 | cd src\LanguageAgnosticTests 9 | python language_agnostic_tester.py ..\Testee\bin\Debug\Testee.exe 10 | 11 | Method 2: generated unit tests 12 | ------------------------------ 13 | 14 | 1. Generate the tests 15 | cd src\LanguageAgnosticTests 16 | generate_tests >..\DocoptNet.Tests\LanguageAgnosticTests.generated.cs 17 | 2. Run all the unit tests 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/OptionNameTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class OptionNameTests 7 | { 8 | [Test] 9 | public void Short_only() 10 | { 11 | Assert.AreEqual("-h", new Option("-h", null).Name); 12 | } 13 | 14 | [Test] 15 | public void Short_and_long() 16 | { 17 | Assert.AreEqual("--help", new Option("-h", "--help").Name); 18 | } 19 | 20 | [Test] 21 | public void Long_only() 22 | { 23 | Assert.AreEqual("--help", new Option(null, "--help").Name); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/DocoptNet/GenerateCodeHelper.cs: -------------------------------------------------------------------------------- 1 | namespace DocoptNet 2 | { 3 | public static class GenerateCodeHelper 4 | { 5 | public static string ConvertDashesToCamelCase(string s) 6 | { 7 | // Start with uppercase char 8 | var makeUpperCase = true; 9 | var result = ""; 10 | for (int i = 0; i < s.Length; i++) 11 | { 12 | if(s[i] == '-') 13 | { 14 | makeUpperCase = true; 15 | continue; 16 | } 17 | 18 | result += makeUpperCase ? char.ToUpperInvariant(s[i]) : s[i]; 19 | makeUpperCase = false; 20 | } 21 | 22 | return result; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/Assumptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using NUnit.Framework; 3 | 4 | namespace DocoptNet.Tests 5 | { 6 | /// 7 | /// Set of tests to validate assumptions about the BCL or other APIs. 8 | /// 9 | [TestFixture] 10 | public class Assumptions 11 | { 12 | [Test] 13 | public void String_Split_with_no_args_should_split_on_white_space() 14 | { 15 | const string TEST_STRING = "first second\tthird\nfourth \n\t\ffifth"; 16 | var s1 = TEST_STRING.Split(); 17 | Assert.AreEqual(8, s1.Length); 18 | var s2 = TEST_STRING.Split(new char[0], StringSplitOptions.RemoveEmptyEntries); 19 | Assert.AreEqual(5, s2.Length); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/DocoptNet/Optional.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DocoptNet 4 | { 5 | internal class Optional : BranchPattern 6 | { 7 | public Optional(params Pattern[] patterns) : base(patterns) 8 | { 9 | 10 | } 11 | 12 | public override MatchResult Match(IList left, IEnumerable collected = null) 13 | { 14 | var c = collected ?? new List(); 15 | var l = left; 16 | foreach (var pattern in Children) 17 | { 18 | var res = pattern.Match(l, c); 19 | l = res.Left; 20 | c = res.Collected; 21 | } 22 | return new MatchResult(true, l, c); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/StringPartitionTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class StringPartitionTests 7 | { 8 | [Test] 9 | public void Should_partition_string() 10 | { 11 | var p = new StringPartition("left=right", "="); 12 | Assert.AreEqual("left", p.LeftString); 13 | Assert.AreEqual("=", p.Separator); 14 | Assert.AreEqual("right", p.RightString); 15 | } 16 | 17 | [Test] 18 | public void Should_partition_string_no_sep_match() 19 | { 20 | var p = new StringPartition("left", "="); 21 | Assert.AreEqual("left", p.LeftString); 22 | Assert.AreEqual("", p.Separator); 23 | Assert.AreEqual("", p.RightString); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/PatternFlatTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class PatternFlatTests 7 | { 8 | [Test] 9 | public void Should_flatten_all_types() 10 | { 11 | Assert.AreEqual( 12 | new Pattern[] {new Argument("N"), new Option("-a"), new Argument("M")}, 13 | new Required(new OneOrMore(new Argument("N")), 14 | new Option("-a"), new Argument("M")).Flat()); 15 | } 16 | 17 | [Test] 18 | public void Should_flatten_specific_type() 19 | { 20 | Assert.AreEqual( 21 | new Pattern[] {new OptionsShortcut()}, 22 | new Required(new Optional(new OptionsShortcut()), 23 | new Optional(new Option("-a"))).Flat()); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/DocoptNet/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Setting ComVisible to false makes the types in this assembly not visible 6 | // to COM components. If you need to access a type in this assembly from 7 | // COM, set the ComVisible attribute to true on that type. 8 | [assembly: ComVisible(false)] 9 | 10 | // The following GUID is for the ID of the typelib if this project is exposed to COM 11 | [assembly: Guid("fba1ac90-b7b0-483a-b881-a8e8b6566765")] 12 | 13 | [assembly: InternalsVisibleTo("DocoptNet.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100915abcad70deeb2af0f6424bf23227473e27c84df5e57319274e9c8f5dba27363c2a6d93fa4883dbbc0a3bf18cdba7b677b9d02fa8ab885ca01adc30be2eec9d00e0655e7e1da16c307e2f3dfd95ad84e85b28765de915f2da1dc8d5f9f359614f2ee7838823a4a0cca9fd3d9425b231dabae00f46fbebcf5b5d6a0f5126efb5")] 14 | -------------------------------------------------------------------------------- /src/DocoptNet/Required.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DocoptNet 4 | { 5 | internal class Required : BranchPattern 6 | { 7 | public Required(params Pattern[] patterns) 8 | : base(patterns) 9 | { 10 | } 11 | 12 | public override MatchResult Match(IList left, 13 | IEnumerable collected = null) 14 | { 15 | var coll = collected ?? new List(); 16 | var l = left; 17 | var c = coll; 18 | foreach (var pattern in Children) 19 | { 20 | var res = pattern.Match(l, c); 21 | l = res.Left; 22 | c = res.Collected; 23 | if (!res.Matched) 24 | return new MatchResult(false, left, coll); 25 | } 26 | return new MatchResult(true, l, c); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/DocoptNet/Either.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace DocoptNet 5 | { 6 | internal class Either : BranchPattern 7 | { 8 | public Either(params Pattern[] patterns) : base(patterns) 9 | { 10 | } 11 | 12 | public override MatchResult Match(IList left, IEnumerable collected = null) 13 | { 14 | var coll = collected ?? new List(); 15 | var outcomes = 16 | Children.Select(pattern => pattern.Match(left, coll)) 17 | .Where(outcome => outcome.Matched) 18 | .ToList(); 19 | if (outcomes.Count != 0) 20 | { 21 | var minCount = outcomes.Min(x => x.Left.Count); 22 | return outcomes.First(x => x.Left.Count == minCount); 23 | } 24 | return new MatchResult(false, left, coll); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/DocoptNet.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp1.1;netcoreapp2.1;net47 5 | ..\DocoptNet\DocoptNet.snk 6 | true 7 | true 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/ParseDefaultsTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class ParseDefaultsTests 7 | { 8 | [Test] 9 | public void Test_parse_defaults() 10 | { 11 | const string DOC = @"usage: prog 12 | Options: 13 | -h, --help Print help message. 14 | -o FILE Output file. 15 | --verbose Verbose mode. 16 | "; 17 | 18 | var expected = new Option[] 19 | {new Option("-h", "--help"), new Option("-o", null, 1), new Option(null, "--verbose")}; 20 | Assert.AreEqual(expected, Docopt.ParseDefaults(DOC)); 21 | } 22 | 23 | [Test] 24 | public void Test_issue_126_defaults_not_parsed_correctly_when_tabs() 25 | { 26 | const string section = "Options:\n\t--foo= [default: bar]"; 27 | var expected = new Option[] { new Option(null, "--foo", 1, "bar")}; 28 | Assert.AreEqual(expected, Docopt.ParseDefaults(section)); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/PatternFixIdentitiesTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class PatternFixIdentitiesTests 7 | { 8 | [Test] 9 | public void Should_fix_all_args() 10 | { 11 | var pattern = new Required(new Argument("N"), new Argument("N")); 12 | Assert.AreEqual(pattern.Children[0], pattern.Children[1]); 13 | Assert.AreNotSame(pattern.Children[0], pattern.Children[1]); 14 | pattern.FixIdentities(); 15 | Assert.AreSame(pattern.Children[0], pattern.Children[1]); 16 | } 17 | 18 | [Test] 19 | public void Should_fix_some_args() 20 | { 21 | var pattern = new Required(new Optional(new Argument("X"), new Argument("N")), new Argument("N")); 22 | Assert.AreEqual(pattern.Children[0].Children[1], pattern.Children[1]); 23 | Assert.AreNotSame(pattern.Children[0].Children[1], pattern.Children[1]); 24 | pattern.FixIdentities(); 25 | Assert.AreSame(pattern.Children[0].Children[1], pattern.Children[1]); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/ShortOptionsErrorHandlingTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class ShortOptionsErrorHandlingTests 7 | { 8 | [Test] 9 | public void Duplicate() 10 | { 11 | Assert.Throws( 12 | () => new Docopt().Apply("Usage: prog -x\nOptions: -x this\n -x that")); 13 | } 14 | 15 | [Test] 16 | public void Non_existent() 17 | { 18 | Assert.Throws( 19 | () => new Docopt().Apply("Usage: prog", "-x")); 20 | } 21 | 22 | [Test] 23 | public void Wrong_opt_with_arg_spec() 24 | { 25 | Assert.Throws( 26 | () => new Docopt().Apply("Usage: prog -o\nOptions: -o ARG")); 27 | } 28 | 29 | [Test] 30 | public void Wrong_missing_arg() 31 | { 32 | Assert.Throws( 33 | () => new Docopt().Apply("Usage: prog -o ARG\nOptions: -o ARG", "-o")); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/DocoptNet/DocoptExitException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NET40 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace DocoptNet 7 | { 8 | #if NET40 9 | [Serializable] 10 | #endif 11 | public class DocoptExitException : DocoptBaseException 12 | { 13 | // 14 | // For guidelines regarding the creation of new exception types, see 15 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp 16 | // and 17 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp 18 | // 19 | 20 | public DocoptExitException() 21 | { 22 | } 23 | public DocoptExitException(string message) : base(message) 24 | { 25 | } 26 | public DocoptExitException(string message, Exception inner) : base(message, inner) 27 | { 28 | } 29 | #if NET40 30 | protected DocoptExitException( 31 | SerializationInfo info, 32 | StreamingContext context) : base(info, context) 33 | { 34 | } 35 | #endif 36 | } 37 | } -------------------------------------------------------------------------------- /src/NuGet/Main.usage.txt: -------------------------------------------------------------------------------- 1 | Example usage for T4 Docopt.NET 2 | 3 | Usage: 4 | prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch] 5 | prog files FILE... 6 | 7 | Options: 8 | -o Short switch. 9 | -s= Short option with arg. 10 | --long=ARG Long option with arg. 11 | --switch Long switch. 12 | 13 | Explanation: 14 | This is an example usage file that needs to be customized. 15 | Every time you change this file, run the Custom Tool command 16 | on T4DocoptNet.tt to re-generate the MainArgs class 17 | (defined in T4DocoptNet.cs). 18 | You can then use the MainArgs classed as follows: 19 | 20 | class Program 21 | { 22 | 23 | static void DoStuff(string arg, bool flagO, string longValue) 24 | { 25 | // ... 26 | } 27 | 28 | static void Main(string[] argv) 29 | { 30 | // Automatically exit(1) if invalid arguments 31 | var args = new MainArgs(argv, exit: true); 32 | if (args.CmdCommand) 33 | { 34 | Console.WriteLine("First command"); 35 | DoStuff(args.ArgArg, args.OptO, args.OptLong); 36 | } 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /src/DocoptNet/DocoptLanguageErrorException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NET40 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace DocoptNet 7 | { 8 | #if NET40 9 | [Serializable] 10 | #endif 11 | public class DocoptLanguageErrorException : DocoptBaseException 12 | { 13 | // 14 | // For guidelines regarding the creation of new exception types, see 15 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp 16 | // and 17 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp 18 | // 19 | 20 | public DocoptLanguageErrorException() 21 | { 22 | } 23 | public DocoptLanguageErrorException(string message) : base(message) 24 | { 25 | } 26 | public DocoptLanguageErrorException(string message, Exception inner) : base(message, inner) 27 | { 28 | } 29 | #if NET40 30 | protected DocoptLanguageErrorException( 31 | SerializationInfo info, 32 | StreamingContext context) : base(info, context) 33 | { 34 | } 35 | #endif 36 | } 37 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/AnyOptionsParameterTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class AnyOptionsParameterTests 7 | { 8 | [Test] 9 | public void Error_long_with_arg() 10 | { 11 | Assert.Throws( 12 | () => new Docopt().Apply("usage: prog [options]", "-foo --bar --spam=eggs")); 13 | 14 | } 15 | 16 | [Test] 17 | public void Error_all_long() 18 | { 19 | Assert.Throws( 20 | () => new Docopt().Apply("usage: prog [options]", "--foo --bar --bar")); 21 | 22 | } 23 | 24 | [Test] 25 | public void Error_short_acc() 26 | { 27 | Assert.Throws( 28 | () => new Docopt().Apply("usage: prog [options]", "--bar --bar --bar -ffff")); 29 | 30 | } 31 | 32 | [Test] 33 | public void Error_all_long_with_arg() 34 | { 35 | Assert.Throws( 36 | () => new Docopt().Apply("usage: prog [options]", "--long=arg --long=another")); 37 | 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Vladimir Keleshev, 2 | Dinh Doan Van Bien, 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software 7 | without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to 10 | whom the Software is furnished to do so, subject to the 11 | following conditions: 12 | 13 | The above copyright notice and this permission notice shall 14 | be included in all copies or substantial portions of the 15 | Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 18 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 19 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 20 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 24 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /src/DocoptNet/DocoptInputErrorException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NET40 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace DocoptNet 7 | { 8 | #if NET40 9 | [Serializable] 10 | #endif 11 | public class DocoptInputErrorException : DocoptBaseException 12 | { 13 | // 14 | // For guidelines regarding the creation of new exception types, see 15 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp 16 | // and 17 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp 18 | // 19 | 20 | public DocoptInputErrorException() 21 | { 22 | } 23 | public DocoptInputErrorException(string message) 24 | : base(message) 25 | { 26 | } 27 | public DocoptInputErrorException(string message, Exception inner) 28 | : base(message, inner) 29 | { 30 | } 31 | #if NET40 32 | protected DocoptInputErrorException( 33 | SerializationInfo info, 34 | StreamingContext context) 35 | : base(info, context) 36 | { 37 | } 38 | #endif 39 | } 40 | } -------------------------------------------------------------------------------- /src/DocoptNet/Command.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace DocoptNet 4 | { 5 | internal class Command : Argument 6 | { 7 | public Command(string name, ValueObject value = null) : base(name, value ?? new ValueObject(false)) 8 | { 9 | } 10 | 11 | public override SingleMatchResult SingleMatch(IList left) 12 | { 13 | for (var i = 0; i < left.Count; i++) 14 | { 15 | var pattern = left[i]; 16 | if (pattern is Argument) 17 | { 18 | if (pattern.Value.ToString() == Name) 19 | return new SingleMatchResult(i, new Command(Name, new ValueObject(true))); 20 | break; 21 | } 22 | } 23 | return new SingleMatchResult(); 24 | } 25 | 26 | public override Node ToNode() { return new CommandNode(this.Name); } 27 | 28 | public override string GenerateCode() 29 | { 30 | var s = Name.ToLowerInvariant(); 31 | s = "Cmd" + GenerateCodeHelper.ConvertDashesToCamelCase(s); 32 | return string.Format("public bool {0} {{ get {{ return _args[\"{1}\"].IsTrue; }} }}", s, Name); 33 | } 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /src/DocoptNet/DocoptBaseException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NET40 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace DocoptNet 7 | { 8 | #if NET40 9 | [Serializable] 10 | #endif 11 | public class DocoptBaseException : Exception 12 | { 13 | // 14 | // For guidelines regarding the creation of new exception types, see 15 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp 16 | // and 17 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp 18 | // 19 | 20 | public DocoptBaseException() 21 | { 22 | } 23 | 24 | public DocoptBaseException(string message) 25 | : base(message) 26 | { 27 | } 28 | 29 | public DocoptBaseException(string message, Exception inner) 30 | : base(message, inner) 31 | { 32 | } 33 | 34 | #if NET40 35 | protected DocoptBaseException( 36 | SerializationInfo info, 37 | StreamingContext context) 38 | : base(info, context) 39 | { 40 | } 41 | #endif 42 | 43 | public int ErrorCode 44 | { 45 | get { return 1; } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/Examples/NavalFate/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using DocoptNet; 3 | 4 | namespace NavalFate 5 | { 6 | internal class Program 7 | { 8 | private const string usage = @"Naval Fate. 9 | 10 | Usage: 11 | naval_fate.exe ship new ... 12 | naval_fate.exe ship move [--speed=] 13 | naval_fate.exe ship shoot 14 | naval_fate.exe mine (set|remove) [--moored | --drifting] 15 | naval_fate.exe (-h | --help) 16 | naval_fate.exe --version 17 | 18 | Options: 19 | -h --help Show this screen. 20 | --version Show version. 21 | --speed= Speed in knots [default: 10]. 22 | --moored Moored (anchored) mine. 23 | --drifting Drifting mine. 24 | 25 | "; 26 | 27 | private static void Main(string[] args) 28 | { 29 | try 30 | { 31 | var arguments = new Docopt().Apply(usage, args, version: "Naval Fate 2.0", exit: true); 32 | foreach (var argument in arguments) 33 | { 34 | Console.WriteLine("{0} = {1}", argument.Key, argument.Value); 35 | } 36 | } 37 | catch (Exception e) 38 | { 39 | Console.WriteLine(e.Message); 40 | } 41 | } 42 | 43 | } 44 | } -------------------------------------------------------------------------------- /src/DocoptNet/BranchPattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | 6 | namespace DocoptNet 7 | { 8 | /// 9 | /// Branch/inner node of a pattern tree. 10 | /// 11 | internal class BranchPattern : Pattern 12 | { 13 | 14 | public BranchPattern(params Pattern[] children) 15 | { 16 | if (children == null) throw new ArgumentNullException("children"); 17 | Children = children; 18 | } 19 | 20 | public override bool HasChildren { get { return true; } } 21 | 22 | public IEnumerable Flat() where T: Pattern 23 | { 24 | return Flat(typeof (T)); 25 | } 26 | 27 | public override ICollection Flat(params Type[] types) 28 | { 29 | if (types == null) throw new ArgumentNullException("types"); 30 | if (types.Contains(this.GetType())) 31 | { 32 | return new Pattern[] { this }; 33 | } 34 | return Children.SelectMany(child => child.Flat(types)).ToList(); 35 | } 36 | 37 | public override string ToString() 38 | { 39 | return string.Format("{0}({1})", GetType().Name, String.Join(", ", Children.Select(c => c == null ? "None" : c.ToString()))); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/DocoptNet/OneOrMore.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace DocoptNet 5 | { 6 | internal class OneOrMore : BranchPattern 7 | { 8 | public OneOrMore(params Pattern[] patterns) 9 | : base(patterns) 10 | { 11 | } 12 | 13 | public override MatchResult Match(IList left, IEnumerable collected = null) 14 | { 15 | Debug.Assert(Children.Count == 1); 16 | var coll = collected ?? new List(); 17 | var l = left; 18 | var c = coll; 19 | IList l_ = null; 20 | var matched = true; 21 | var times = 0; 22 | while (matched) 23 | { 24 | // could it be that something didn't match but changed l or c? 25 | var res = Children[0].Match(l, c); 26 | matched = res.Matched; 27 | l = res.Left; 28 | c = res.Collected; 29 | times += matched ? 1 : 0; 30 | if (l_ != null && l_.Equals(l)) 31 | break; 32 | l_ = l; 33 | } 34 | if (times >= 1) 35 | { 36 | return new MatchResult(true, l, c); 37 | } 38 | return new MatchResult(false, left, coll); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/DocoptNet/StringPartition.cs: -------------------------------------------------------------------------------- 1 | namespace DocoptNet 2 | { 3 | public struct StringPartition 4 | { 5 | public string LeftString; 6 | public string Separator; 7 | public string RightString; 8 | 9 | /// 10 | /// Split the at the first occurrence of , and stores the part before the separator, 11 | /// the separator itself, and the part after the separator. If the separator is not found, stores the string itself, and 12 | /// two empty strings. 13 | /// 14 | /// 15 | /// 16 | public StringPartition(string stringToPartition, string separator) 17 | { 18 | LeftString = stringToPartition; 19 | Separator = ""; 20 | RightString = ""; 21 | 22 | var i = stringToPartition.IndexOf(separator, System.StringComparison.Ordinal); 23 | if (i > 0) 24 | { 25 | LeftString = stringToPartition.Substring(0, i); 26 | Separator = separator; 27 | RightString = stringToPartition.Substring(i + separator.Length); 28 | } 29 | } 30 | 31 | public bool NoSeparatorFound 32 | { 33 | get { return Separator=="" && RightString == ""; } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/PatternFixRepeatingArgumentsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using NUnit.Framework; 3 | 4 | namespace DocoptNet.Tests 5 | { 6 | [TestFixture] 7 | public class PatternFixRepeatingArgumentsTests 8 | { 9 | [Test] 10 | public void Should_fix_option() 11 | { 12 | Assert.AreEqual( 13 | new Option("-a"), 14 | new Option("-a").FixRepeatingArguments() 15 | ); 16 | } 17 | 18 | [Test] 19 | public void Should_fix_arg() 20 | { 21 | Assert.AreEqual( 22 | new Argument("A"), 23 | new Argument("A").FixRepeatingArguments() 24 | ); 25 | } 26 | 27 | [Test] 28 | public void Should_fix_required_args() 29 | { 30 | Assert.AreEqual( 31 | new Required(new Argument("N", new ArrayList()), new Argument("N", new ArrayList())), 32 | new Required(new Argument("N"), new Argument("N")).FixRepeatingArguments() 33 | ); 34 | } 35 | 36 | [Test] 37 | public void Should_fix_either_args() 38 | { 39 | Assert.AreEqual( 40 | new Either(new Argument("N", new ArrayList()), 41 | new OneOrMore(new Argument("N", new ArrayList()))), 42 | new Either(new Argument("N"), new OneOrMore(new Argument("N"))).Fix() 43 | ); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/GenerateCodeHelperTest.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class GenerateCodeHelperTest 7 | { 8 | [Test] 9 | public void ConvertDashesToCamelCase_single_dashes() 10 | { 11 | var actual = GenerateCodeHelper.ConvertDashesToCamelCase("string-with-dashes"); 12 | Assert.AreEqual("StringWithDashes", actual); 13 | } 14 | 15 | [Test] 16 | public void ConvertDashesToCamelCase_consecutive_dashes() 17 | { 18 | var input = "string--with----dashes"; 19 | var expected = "StringWithDashes"; 20 | var actual = GenerateCodeHelper.ConvertDashesToCamelCase(input); 21 | Assert.AreEqual(expected, actual); 22 | } 23 | 24 | [Test] 25 | public void ConvertDashesToCamelCase_existing_uppercase_letters() 26 | { 27 | var input = "string-With-Dashes"; 28 | var expected = "StringWithDashes"; 29 | var actual = GenerateCodeHelper.ConvertDashesToCamelCase(input); 30 | Assert.AreEqual(expected, actual); 31 | } 32 | 33 | [Test] 34 | public void ConvertDashesToCamelCase_all_uppercase_letters() 35 | { 36 | var input = "STRING-WITH-DASHES"; 37 | var expected = "STRINGWITHDASHES"; 38 | var actual = GenerateCodeHelper.ConvertDashesToCamelCase(input); 39 | Assert.AreEqual(expected, actual); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/Testee/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Testee")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Testee")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("c47d4981-d4ac-4f8d-b130-ba1625434bb0")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/Examples/NavalFate/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NavalFate")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("NavalFate")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("e681ff82-f89d-4c62-97a5-e7d3c609333f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("T4DocoptNetHostApp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("T4DocoptNetHostApp")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("98f7dff8-2d92-454c-8a76-e8ecd7061923")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/DefaultValueForPositionalArgumentsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace DocoptNet.Tests 5 | { 6 | [TestFixture] 7 | public class DefaultValueForPositionalArgumentsTests 8 | { 9 | [Test] 10 | public void Expect_default_value() 11 | { 12 | var expected = new Dictionary 13 | { 14 | {"--data", new ValueObject(new[] {"x"})} 15 | }; 16 | var actual = new Docopt().Apply("Usage: prog [--data=...]\n\nOptions:\n\t-d --data= Input data [default: x]", ""); 17 | Assert.AreEqual(expected, actual); 18 | } 19 | 20 | [Test] 21 | public void Expect_default_collection() 22 | { 23 | var expected = new Dictionary 24 | { 25 | {"--data", new ValueObject(new string[] {"x", "y"})} 26 | }; 27 | var actual = new Docopt().Apply("Usage: prog [--data=...]\n\n Options:\n\t-d --data= Input data [default: x y]", ""); 28 | Assert.AreEqual(expected, actual); 29 | } 30 | 31 | [Test] 32 | public void Expect_one_arg() 33 | { 34 | var expected = new Dictionary 35 | { 36 | {"--data", new ValueObject(new[] {"this"})} 37 | }; 38 | var actual = new Docopt().Apply("Usage: prog [--data=...]\n\n Options:\n\t-d --data= Input data [default: x y]", "--data=this"); 39 | Assert.AreEqual(expected, actual); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/OptionsFirstTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace DocoptNet.Tests 5 | { 6 | [TestFixture] 7 | public class OptionsFirstTests 8 | { 9 | [Test] 10 | public void Match_opt_first() 11 | { 12 | var expected = new Dictionary 13 | { 14 | {"--opt", new ValueObject(true)}, 15 | {"", new ValueObject(new[]{"this", "that"})} 16 | }; 17 | var actual = new Docopt().Apply("usage: prog [--opt] [...]", "--opt this that"); 18 | Assert.AreEqual(expected, actual); 19 | } 20 | 21 | [Test] 22 | public void Match_args_first() 23 | { 24 | var expected = new Dictionary 25 | { 26 | {"--opt", new ValueObject(true)}, 27 | {"", new ValueObject(new[]{"this", "that"})} 28 | }; 29 | var actual = new Docopt().Apply("usage: prog [--opt] [...]", "this that --opt"); 30 | Assert.AreEqual(expected, actual); 31 | } 32 | 33 | [Test] 34 | public void Check_options_first() 35 | { 36 | var expected = new Dictionary 37 | { 38 | {"--opt", new ValueObject(false)}, 39 | {"", new ValueObject(new[]{"this", "that", "--opt"})} 40 | }; 41 | var actual = new Docopt().Apply("usage: prog [--opt] [...]", "this that --opt", optionsFirst: true); 42 | Assert.AreEqual(expected, actual); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/LanguageAgnosticTests/generate_tests.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | # 4 | # Quick and dirty script to generate a C# test suite 5 | # out of testcases.docopt 6 | # 7 | # e.g. generate_tests >LanguageAgnosticTests.generated.cs 8 | # 9 | 10 | import sys, json, re, os 11 | 12 | fixtures = open(os.path.join(os.path.dirname(__file__), 'testcases.docopt'), 'r').read() 13 | 14 | # remove comments 15 | fixtures = re.sub('#.*$', '', fixtures, flags=re.M) 16 | 17 | ids = [int(x) for x in sys.argv[1:]] if len(sys.argv) > 1 else None 18 | 19 | fmt = """ 20 | [Test] 21 | public void Test_%d() 22 | { 23 | var doc = @"%s"; 24 | var actual = Docopt(doc, @"%s"); 25 | var expected = @"%s"; 26 | CheckResult(expected, actual); 27 | } 28 | """ 29 | 30 | print ("""using System.Collections.Generic; 31 | using NUnit.Framework; 32 | 33 | namespace DocoptNet.Tests 34 | { 35 | [TestFixture] 36 | public partial class LanguageAgnosticTests 37 | { 38 | #region Language agnostic tests generated code 39 | """) 40 | 41 | index = 0 42 | for fixture in fixtures.split('r"""'): 43 | doc, _, body = fixture.partition('"""') 44 | for case in body.split('$')[1:]: 45 | index += 1 46 | if ids is not None and index not in ids: 47 | continue 48 | argv, _, expect = case.strip().partition('\n') 49 | prog, _, argv = argv.strip().partition(' ') 50 | assert prog == 'prog', repr(prog) 51 | doc = doc.replace('"', '""') 52 | expect = expect.replace('"', '""') 53 | argv = argv.replace('"', '"""') 54 | print (fmt % (index,doc,argv,expect)) 55 | 56 | print (""" 57 | #endregion 58 | } 59 | }""") 60 | -------------------------------------------------------------------------------- /src/DocoptNet/MatchResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace DocoptNet 6 | { 7 | internal class MatchResult 8 | { 9 | public bool Matched; 10 | public IList Left; 11 | public IEnumerable Collected; 12 | 13 | public MatchResult() { } 14 | 15 | public MatchResult(bool matched, IList left, IEnumerable collected) 16 | { 17 | Matched = matched; 18 | Left = left; 19 | Collected = collected; 20 | } 21 | 22 | public bool LeftIsEmpty { get { return Left.Count == 0; } } 23 | 24 | public override bool Equals(object obj) 25 | { 26 | // 27 | // See the full list of guidelines at 28 | // http://go.microsoft.com/fwlink/?LinkID=85237 29 | // and also the guidance for operator== at 30 | // http://go.microsoft.com/fwlink/?LinkId=85238 31 | // 32 | 33 | if (obj == null || GetType() != obj.GetType()) 34 | { 35 | return false; 36 | } 37 | 38 | return ToString().Equals(obj.ToString()); 39 | } 40 | 41 | public override int GetHashCode() 42 | { 43 | return ToString().GetHashCode(); 44 | } 45 | 46 | public override string ToString() 47 | { 48 | return string.Format("matched={0} left=[{1}], collected=[{2}]", 49 | Matched, 50 | Left == null ? "" : string.Join(", ", Left.Select(p => p.ToString())), 51 | Collected == null ? "" : string.Join(", ", Collected.Select(p => p.ToString())) 52 | ); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/RequiredMatchTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class RequiredMatchTests 7 | { 8 | [Test] 9 | public void Should_match_option() 10 | { 11 | Assert.AreEqual( 12 | new MatchResult(true, 13 | new Pattern[0], 14 | new Pattern[] {new Option("-a")}), 15 | new Required(new Option("-a")).Match(new Pattern[] {new Option("-a")}) 16 | ); 17 | } 18 | 19 | [Test] 20 | public void Should_not_match_empty() 21 | { 22 | Assert.AreEqual( 23 | new MatchResult(false, 24 | new Pattern[0], 25 | new Pattern[0]), 26 | new Required(new Option("-a")).Match(new Pattern[0]) 27 | ); 28 | } 29 | 30 | [Test] 31 | public void Should_not_match_other_option() 32 | { 33 | Assert.AreEqual( 34 | new MatchResult(false, 35 | new Pattern[] {new Option("-x")}, 36 | new Pattern[0] 37 | ), 38 | new Required(new Option("-a")).Match(new Pattern[] {new Option("-x")}) 39 | ); 40 | } 41 | 42 | [Test] 43 | public void Should_not_match_2_required() 44 | { 45 | Assert.AreEqual( 46 | new MatchResult(false, 47 | new Pattern[] { new Option("-a") }, 48 | new Pattern[0] 49 | ), 50 | new Required(new Option("-a"), new Option("-b")).Match(new Pattern[] { new Option("-a") }) 51 | ); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/LanguageAgnosticTests/language_agnostic_tester.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import sys, json, re, os 3 | from subprocess import Popen, PIPE, STDOUT 4 | 5 | fixtures = open(os.path.join(os.path.dirname(__file__), 'testcases.docopt'), 'r').read() 6 | 7 | # remove comments 8 | fixtures = re.sub('#.*$', '', fixtures, flags=re.M) 9 | 10 | testee = (sys.argv[1] if len(sys.argv) >= 2 else 11 | exit('Usage: language_agnostic_tester.py ./path/to/executable/testee [ID ...]')) 12 | ids = [int(x) for x in sys.argv[2:]] if len(sys.argv) > 2 else None 13 | summary = '' 14 | 15 | index = 0 16 | for fixture in fixtures.split('r"""'): 17 | doc, _, body = fixture.partition('"""') 18 | for case in body.split('$')[1:]: 19 | index += 1 20 | if ids is not None and index not in ids: 21 | continue 22 | argv, _, expect = case.strip().partition('\n') 23 | prog, _, argv = argv.strip().partition(' ') 24 | assert prog == 'prog', repr(prog) 25 | p = Popen(testee + ' ' + argv, 26 | stdout=PIPE, stdin=PIPE, stderr=STDOUT, shell=True) 27 | result = p.communicate(input=doc)[0] 28 | try: 29 | py_result = json.loads(result) 30 | py_expect = json.loads(expect) 31 | except: 32 | summary += 'J' 33 | print (' %d: BAD JSON ' % index).center(79, '=') 34 | print 'result>', result 35 | print 'expect>', expect 36 | continue 37 | if py_result == py_expect: 38 | summary += '.' 39 | else: 40 | print (' %d: FAILED ' % index).center(79, '=') 41 | print 'r"""%s"""' % doc 42 | print '$ prog %s\n' % argv 43 | print 'result>', result 44 | print 'expect>', expect 45 | summary += 'F' 46 | 47 | print (' %d / %d ' % (summary.count('.'), len(summary))).center(79, '=') 48 | print summary 49 | -------------------------------------------------------------------------------- /src/DocoptNet/Argument.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace DocoptNet 5 | { 6 | internal class Argument: LeafPattern 7 | { 8 | public Argument(string name, ValueObject value = null) : base(name, value) 9 | { 10 | } 11 | 12 | public Argument(string name, string value) 13 | : base(name, new ValueObject(value)) 14 | { 15 | } 16 | 17 | public Argument(string name, ICollection coll) 18 | : base(name, new ValueObject(coll)) 19 | { 20 | } 21 | 22 | public Argument(string name, int value) 23 | : base(name, new ValueObject(value)) 24 | { 25 | } 26 | 27 | public override SingleMatchResult SingleMatch(IList left) 28 | { 29 | for (var i = 0; i < left.Count; i++) 30 | { 31 | if (left[i] is Argument) 32 | return new SingleMatchResult(i, new Argument(Name, left[i].Value)); 33 | } 34 | return new SingleMatchResult(); 35 | } 36 | 37 | public override Node ToNode() 38 | { 39 | return new ArgumentNode(this.Name, (this.Value != null && this.Value.IsList) ? ValueType.List : ValueType.String); 40 | } 41 | 42 | public override string GenerateCode() 43 | { 44 | var s = Name.Replace("<", "").Replace(">", " ").ToLowerInvariant(); 45 | s = "Arg" + GenerateCodeHelper.ConvertDashesToCamelCase(s); 46 | 47 | if (Value != null && Value.IsList) 48 | { 49 | return string.Format("public ArrayList {0} {{ get {{ return _args[\"{1}\"].AsList; }} }}", s, Name); 50 | } 51 | return string.Format("public string {0} {{ get {{ return null == _args[\"{1}\"] ? null : _args[\"{1}\"].ToString(); }} }}", s, Name); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/ArgumentMatchTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class ArgumentMatchTests 7 | { 8 | [Test] 9 | public void Should_match_arg() 10 | { 11 | Assert.AreEqual( 12 | new MatchResult(true, new Pattern[0], new Pattern[] {new Argument("N", new ValueObject(9))}), 13 | new Argument("N").Match(new Pattern[] {new Argument(null, new ValueObject(9))}) 14 | ); 15 | } 16 | 17 | [Test] 18 | public void Should_not_match_arg() 19 | { 20 | Assert.AreEqual( 21 | new MatchResult(false, new Pattern[] {new Option("-x")}, new Pattern[0]), 22 | new Argument("N").Match(new Pattern[] {new Option("-x")}) 23 | ); 24 | } 25 | 26 | [Test] 27 | public void Should_match_arg_after_opts() 28 | { 29 | Assert.AreEqual( 30 | new MatchResult(true, new Pattern[] {new Option("-x"), new Option("-a")}, 31 | new Pattern[] {new Argument("N", new ValueObject(5))}), 32 | new Argument("N").Match(new Pattern[] 33 | { 34 | new Option("-x"), 35 | new Option("-a"), 36 | new Argument(null, new ValueObject(5)) 37 | }) 38 | ); 39 | } 40 | 41 | [Test] 42 | public void Should_match_first_arg_only() 43 | { 44 | Assert.AreEqual( 45 | new MatchResult(true, 46 | new Pattern[] {new Argument(null, new ValueObject(0))}, 47 | new Pattern[] {new Argument("N", new ValueObject(9))}), 48 | new Argument("N").Match(new Pattern[] 49 | {new Argument(null, new ValueObject(9)), new Argument(null, new ValueObject(0))}) 50 | ); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/CommandMatchTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class CommandMatchTests 7 | { 8 | [Test] 9 | public void Should_match_command() 10 | { 11 | Assert.AreEqual( 12 | new MatchResult(true, 13 | new Pattern[0], 14 | new Pattern[] {new Command("c", new ValueObject(true))}), 15 | new Command("c").Match(new Pattern[] {new Argument(null, new ValueObject("c"))}) 16 | ); 17 | } 18 | 19 | [Test] 20 | public void Should_not_match_command() 21 | { 22 | Assert.AreEqual( 23 | new MatchResult(false, 24 | new Pattern[] {new Option("-x")}, 25 | new Pattern[0]), 26 | new Command("c").Match(new Pattern[] {new Option("-x")}) 27 | ); 28 | } 29 | 30 | [Test] 31 | public void Should_match_command_after_options() 32 | { 33 | Assert.AreEqual( 34 | new MatchResult(true, 35 | new Pattern[] { new Option("-x"), new Option("-a") }, 36 | new Pattern[] { new Command("c", new ValueObject(true)) }), 37 | new Command("c").Match(new Pattern[] { new Option("-x"), new Option("-a"), new Argument(null, new ValueObject("c")) }) 38 | ); 39 | } 40 | 41 | [Test] 42 | public void Should_match_either_command() 43 | { 44 | Assert.AreEqual( 45 | new MatchResult(true, 46 | new Pattern[0], 47 | new Pattern[] { new Command("rm", new ValueObject(true)) }), 48 | new Either(new Command("add"), new Command("rm")).Match(new Pattern[] { new Argument(null, new ValueObject("rm")) }) 49 | ); 50 | } 51 | 52 | } 53 | } -------------------------------------------------------------------------------- /src/NuGet/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | # Set the build action type to None 3 | $project.ProjectItems.Item("T4DocoptNet.tt").Properties.Item("BuildAction").Value = 0 #prjBuildActionNone 4 | $project.ProjectItems.Item("T4DocoptNet.tt.hooks.t4").Properties.Item("BuildAction").Value = 0 5 | $project.ProjectItems.Item("T4DocoptNet.tt.settings.xml").Properties.Item("BuildAction").Value = 0 6 | 7 | # Edit the project's T4DocoptNet.tt file to point to the right DocoptNet assembly path. 8 | function Set-DocoptNetAssemblyPath($AssemblyPath, $ProjectDirectoryPath) 9 | { 10 | # Get the path to the T4DocoptNet.tt file. 11 | Write-Debug "Project directory is '$ProjectDirectoryPath'." 12 | $t4FilePath = Join-Path $ProjectDirectoryPath "T4DocoptNet.tt" 13 | 14 | # If we found the T4 file, try and update it. 15 | if (Test-Path -Path $t4FilePath) 16 | { 17 | Write-Debug "Found T4 file at '$t4FilePath'." 18 | 19 | # Load the packages.config xml document and grab all of the elements. 20 | $wholeFile = [System.IO.File]::ReadAllText($t4FilePath) 21 | $searchString = '\$\(ProjectDir\)\$\(OutDir\)DocoptNet.dll' 22 | if (!($wholeFile -match $searchString)) 23 | { 24 | Write-Debug "Could not find search string in '$t4FilePath'." 25 | return 26 | } 27 | 28 | Write-Debug "Setting assembly path '$AssemblyPath' in '$t4FilePath'." 29 | $wholeFile = $wholeFile -replace $searchString, $AssemblyPath 30 | 31 | # Save the T4 file back now that we've changed it. 32 | [System.IO.File]::WriteAllText($t4FilePath, $wholeFile) 33 | } 34 | # Else we coudn't find the T4 file for some reason, so error out. 35 | else 36 | { 37 | Write-Debug "Could not find T4 file at '$t4FilePath'." 38 | } 39 | } 40 | 41 | $p = (gci -Path $installPath -Filter net40 -recurse | select -First 1).FullName | Join-Path -ChildPath "DocoptNet.dll" 42 | Set-DocoptNetAssemblyPath -AssemblyPath $p -ProjectDirectoryPath ([System.IO.Directory]::GetParent($project.FullName)) 43 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/UsageTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NUnit.Framework; 3 | 4 | namespace DocoptNet.Tests 5 | { 6 | [TestFixture] 7 | public class UsageTests 8 | { 9 | [Test] 10 | public void Test_formal_usage() 11 | { 12 | const string DOC = 13 | "Usage: prog [-hv] ARG\r\n" + 14 | " prog N M\r\n" + 15 | "\r\n" + 16 | " prog is a program."; 17 | var usage = Docopt.ParseSection("usage:", DOC).First(); 18 | Assert.AreEqual("Usage: prog [-hv] ARG\r\n prog N M", usage); 19 | Assert.AreEqual("( [-hv] ARG ) | ( N M )", Docopt.FormalUsage(usage)); 20 | } 21 | [Test] 22 | public void Should_parse_usage_section_correctly() 23 | { 24 | const string USAGE = @"usage: this 25 | 26 | usage:hai 27 | usage: this that 28 | 29 | usage: foo 30 | bar 31 | 32 | PROGRAM USAGE: 33 | foo 34 | bar 35 | " + 36 | "usage:\r\n\ttoo\r\n" + 37 | "\ttar\r\n" + 38 | @"Usage: eggs spam 39 | BAZZ 40 | usage: pit stop 41 | "; 42 | 43 | 44 | Assert.AreEqual(new string[] {}, Docopt.ParseSection("usage:", "foo bar fizz buzz"), "No usage"); 45 | 46 | Assert.AreEqual(new[] {"usage: prog"}, Docopt.ParseSection("usage:", "usage: prog"), "One line usage"); 47 | 48 | Assert.AreEqual(new[] {@"usage: -args\r\n -y"}, Docopt.ParseSection("usage:", @"usage: -args\r\n -y"), 49 | "Multi line usage"); 50 | 51 | Assert.AreEqual(new[] 52 | { 53 | "usage: this", 54 | "usage:hai", 55 | "usage: this that", 56 | @"usage: foo 57 | bar", 58 | @"PROGRAM USAGE: 59 | foo 60 | bar", 61 | "usage:\r\n\ttoo\r\n\ttar", 62 | "Usage: eggs spam", 63 | "usage: pit stop" 64 | }, Docopt.ParseSection("usage:", USAGE), "Variations on casing, spaces and tabs"); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/LongOptionsErrorHandlingTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class LongOptionsErrorHandlingTests 7 | { 8 | [Test] 9 | public void Non_existent() 10 | { 11 | Assert.Throws(() => 12 | new Docopt().Apply("Usage: prog", "--non-existent") 13 | ); 14 | } 15 | 16 | [Test] 17 | public void Wrong_long_opt() 18 | { 19 | Assert.Throws( 20 | () => new Docopt().Apply( 21 | "Usage: prog [--version --verbose]\n" + 22 | "Options: --version\n" + 23 | " --verbose", "--ver") 24 | ); 25 | } 26 | 27 | [Test] 28 | public void Missing_long_opt_arg_spec() 29 | { 30 | Assert.Throws( 31 | () => new Docopt().Apply( 32 | "Usage: prog --long\n" + 33 | "Options: --long ARG\n", "") 34 | ); 35 | } 36 | 37 | [Test] 38 | public void Missing_long_opt_arg() 39 | { 40 | Assert.Throws( 41 | () => new Docopt().Apply( 42 | "Usage: prog --long ARG\n" + 43 | "Options: --long ARG\n", "--long") 44 | ); 45 | } 46 | 47 | [Test] 48 | public void Missing_long_opt_arg_spec2() 49 | { 50 | Assert.Throws( 51 | () => new Docopt().Apply( 52 | "Usage: prog --long=ARG\n" + 53 | "Options: --long\n", "") 54 | ); 55 | } 56 | 57 | [Test] 58 | public void Unexpected_arg() 59 | { 60 | Assert.Throws( 61 | () => new Docopt().Apply( 62 | "Usage: prog --long\n" + 63 | "Options: --long\n", "--long=ARG") 64 | ); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/Testee/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using DocoptNet; 7 | using Newtonsoft.Json; 8 | 9 | namespace Testee 10 | { 11 | internal class Program 12 | { 13 | public static string Docopt(string doc, string[] cmdLine) 14 | { 15 | try 16 | { 17 | var arguments = new Docopt().Apply(doc, cmdLine); 18 | var dict = new Dictionary(); 19 | foreach (var argument in arguments) 20 | { 21 | if (argument.Value == null) 22 | dict[argument.Key] = null; 23 | else if (argument.Value.IsList) 24 | { 25 | var l = new ArrayList(); 26 | foreach (var v in argument.Value.AsList) 27 | { 28 | if (v is ValueObject) 29 | l.Add(((v) as ValueObject).Value); 30 | else 31 | l.Add(v); 32 | } 33 | dict[argument.Key] = l; 34 | } 35 | else 36 | dict[argument.Key] = argument.Value.Value; 37 | } 38 | return JsonConvert.SerializeObject(dict); 39 | } 40 | catch (Exception) 41 | { 42 | return "\"user-error\""; 43 | } 44 | } 45 | 46 | private static void Main(string[] args) 47 | { 48 | Stream inputStream = Console.OpenStandardInput(); 49 | var bytes = new byte[100]; 50 | var sb = new StringBuilder(); 51 | int outputLength = inputStream.Read(bytes, 0, 100); 52 | while (outputLength > 0) 53 | { 54 | char[] chars = Encoding.UTF8.GetChars(bytes, 0, outputLength); 55 | sb.Append(chars); 56 | outputLength = inputStream.Read(bytes, 0, 100); 57 | } 58 | var doc = sb.ToString(); 59 | var s = Docopt(doc, args); 60 | Console.WriteLine(s); 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/ListArgumentMatchTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class ListArgumentMatchTests 7 | { 8 | [Test] 9 | public void Should_match_required_two_args_into_a_list() 10 | { 11 | Assert.AreEqual( 12 | new MatchResult(true, 13 | new Pattern[0], 14 | new Pattern[] { new Argument("N", new [] { "1", "2"}) }), 15 | new Required(new Argument("N"), new Argument("N")).Fix().Match(new Pattern[] { new Argument(null, "1"), new Argument(null, "2") }) 16 | ); 17 | } 18 | 19 | [Test] 20 | public void Should_match_oneormore_arg_into_a_list() 21 | { 22 | Assert.AreEqual( 23 | new MatchResult(true, 24 | new Pattern[0], 25 | new Pattern[] { new Argument("N", new[] { "1", "2", "3" }) }), 26 | new OneOrMore(new Argument("N")).Fix().Match(new Pattern[] { new Argument(null, "1"), new Argument(null, "2"), new Argument(null, "3") }) 27 | ); 28 | } 29 | 30 | [Test] 31 | public void Should_match_required_and_oneormore_arg_into_a_list() 32 | { 33 | Assert.AreEqual( 34 | new MatchResult(true, 35 | new Pattern[0], 36 | new Pattern[] { new Argument("N", new[] { "1", "2", "3" }) }), 37 | new Required(new Argument("N"), new OneOrMore(new Argument("N"))).Fix().Match(new Pattern[] { new Argument(null, "1"), new Argument(null, "2"), new Argument(null, "3") }) 38 | ); 39 | } 40 | 41 | [Test] 42 | public void Should_match_doubly_required_arg_into_a_list() 43 | { 44 | Assert.AreEqual( 45 | new MatchResult(true, 46 | new Pattern[0], 47 | new Pattern[] { new Argument("N", new[] { "1", "2" }) }), 48 | new Required(new Argument("N"), new Required(new Argument("N"))).Fix().Match(new Pattern[] { new Argument(null, "1"), new Argument(null, "2")}) 49 | ); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/BasicPatternMatchingTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class BasicPatternMatchingTests 7 | { 8 | // ( -a N [ -x Z ] ) 9 | private readonly Pattern _pattern = new Required(new Option("-a"), new Argument("N"), 10 | new Optional(new Option("-x"), new Argument("Z"))); 11 | 12 | [Test] 13 | public void Should_match_required() 14 | { 15 | // -a N 16 | Assert.AreEqual( 17 | new MatchResult(true, 18 | new Pattern[0], 19 | new Pattern[] {new Option("-a"), new Argument("N", "9")}), 20 | _pattern.Match(new Pattern[] {new Option("-a"), new Argument(null, "9")}) 21 | ); 22 | } 23 | 24 | [Test] 25 | public void Should_match_required_and_optional() 26 | { 27 | // -a -x N Z 28 | Assert.AreEqual( 29 | new MatchResult(true, 30 | new Pattern[0], 31 | new Pattern[] 32 | { 33 | new Option("-a"), new Argument("N", "9"), 34 | new Option("-x"), new Argument("Z", "5") 35 | }), 36 | _pattern.Match(new Pattern[] 37 | {new Option("-a"), new Option("-x"), new Argument(null, "9"), new Argument(null, "5")}) 38 | ); 39 | } 40 | 41 | [Test] 42 | public void Should_not_match_spurious_arg() 43 | { 44 | // -x N Z 45 | Assert.AreEqual( 46 | new MatchResult(false, 47 | new Pattern[] 48 | { 49 | new Option("-x"), new Argument(null, "9"), new Argument(null, "5") 50 | }, 51 | new Pattern[0] 52 | ), 53 | _pattern.Match(new Pattern[] {new Option("-x"), new Argument(null, "9"), new Argument(null, "5")}) 54 | ); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/OptionMatchTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class OptionMatchTests 7 | { 8 | [Test] 9 | public void Test_option_match_opt_matched() 10 | { 11 | var expected = new MatchResult(true, new Pattern[0], new[] {new Option("-a", value: new ValueObject(true))}); 12 | var actual = new Option("-a").Match(new[] {new Option("-a", value: new ValueObject(true))}); 13 | Assert.AreEqual(expected, actual); 14 | } 15 | 16 | [Test] 17 | public void Test_option_match_opt_no_match() 18 | { 19 | var expected = new MatchResult(false, new[] {new Option("-x")}, new Pattern[0]); 20 | var actual = new Option("-a").Match(new[] {new Option("-x")}); 21 | Assert.AreEqual(expected, actual); 22 | } 23 | 24 | [Test] 25 | public void Test_option_match_opt_no_match_with_arg() 26 | { 27 | var expected = new MatchResult(false, new[] {new Argument("N"),}, new Pattern[0]); 28 | var actual = new Option("-a").Match(new[] {new Argument("N")}); 29 | Assert.AreEqual(expected, actual); 30 | } 31 | 32 | [Test] 33 | public void Test_option_match_one_opt_out_of_two() 34 | { 35 | var expected = new MatchResult(true, 36 | new Pattern[] {new Option("-x"), new Argument("N")}, 37 | new[] {new Option("-a")} 38 | ); 39 | var actual = new Option("-a").Match(new Pattern[] {new Option("-x"), new Option("-a"), new Argument("N")}); 40 | Assert.AreEqual(expected, actual); 41 | } 42 | 43 | [Test] 44 | public void Test_option_match_same_opt_one_with_value() 45 | { 46 | var expected = new MatchResult(true, 47 | new Pattern[] {new Option("-a")}, 48 | new Pattern[] {new Option("-a", value: new ValueObject(true))} 49 | ); 50 | var actual = 51 | new Option("-a").Match(new Pattern[] { new Option("-a", value: new ValueObject(true)), new Option("-a") }); 52 | Assert.AreEqual(expected, actual); 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/LanguageAgnosticTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using NUnit.Framework; 5 | using Newtonsoft.Json; 6 | 7 | namespace DocoptNet.Tests 8 | { 9 | [TestFixture] 10 | public partial class LanguageAgnosticTests 11 | { 12 | public string Docopt(string doc, string cmdLine) 13 | { 14 | try 15 | { 16 | var arguments = new Docopt().Apply(doc, cmdLine); 17 | var dict = new Dictionary(); 18 | foreach (var argument in arguments) 19 | { 20 | if (argument.Value == null) 21 | dict[argument.Key] = null; 22 | else if (argument.Value.IsList) 23 | { 24 | var l = new ArrayList(); 25 | foreach (var v in argument.Value.AsList) 26 | { 27 | if (v is ValueObject) 28 | l.Add(((v) as ValueObject).Value); 29 | else 30 | l.Add(v); 31 | } 32 | dict[argument.Key] = l; 33 | } 34 | else 35 | dict[argument.Key] = argument.Value.Value; 36 | } 37 | return JsonConvert.SerializeObject(dict); 38 | } 39 | catch (Exception) 40 | { 41 | return "\"user-error\""; 42 | } 43 | } 44 | 45 | public void CheckResult(string expectedJson, string resultJson) 46 | { 47 | if (expectedJson.StartsWith("{")) 48 | { 49 | var expectedDict = JsonConvert.DeserializeObject>(expectedJson); 50 | var actualDict = JsonConvert.DeserializeObject>(resultJson); 51 | Assert.AreEqual(expectedDict, actualDict); 52 | } 53 | else 54 | { 55 | var expected = JsonConvert.DeserializeObject(expectedJson).ToString(); 56 | var actual = JsonConvert.DeserializeObject(resultJson).ToString(); 57 | Assert.AreEqual(expected, actual); 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/EitherMatchTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class EitherMatchTests 7 | { 8 | [Test] 9 | public void Should_match_option() 10 | { 11 | Assert.AreEqual( 12 | new MatchResult(true, 13 | new Pattern[0], 14 | new Pattern[] { new Option("-a") }), 15 | new Either(new Option("-a"), new Option("-b")).Match(new Pattern[] { new Option("-a") }) 16 | ); 17 | } 18 | 19 | [Test] 20 | public void Should_match_first_option() 21 | { 22 | Assert.AreEqual( 23 | new MatchResult(true, 24 | new Pattern[] { new Option("-b") }, 25 | new Pattern[] { new Option("-a") }), 26 | new Either(new Option("-a"), new Option("-b")).Match(new Pattern[] { new Option("-a"), new Option("-b"), }) 27 | ); 28 | } 29 | 30 | [Test] 31 | public void Should_not_match_other_option() 32 | { 33 | Assert.AreEqual( 34 | new MatchResult(false, 35 | new Pattern[] { new Option("-x") }, 36 | new Pattern[0]), 37 | new Either(new Option("-a"), new Option("-b")).Match(new Pattern[] { new Option("-x") }) 38 | ); 39 | } 40 | 41 | [Test] 42 | public void Should_match_one_option_out_of_three() 43 | { 44 | Assert.AreEqual( 45 | new MatchResult(true, 46 | new Pattern[] { new Option("-x") }, 47 | new Pattern[] { new Option("-b") }), 48 | new Either(new Option("-a"), new Option("-b"), new Option("-c")).Match(new Pattern[] { new Option("-x"), new Option("-b") }) 49 | ); 50 | } 51 | 52 | [Test] 53 | public void Should_match_required_args() 54 | { 55 | Assert.AreEqual( 56 | new MatchResult(true, 57 | new Pattern[0], 58 | new Pattern[] { new Argument("N", 1), new Argument("M", 2) }), 59 | new Either(new Argument("M"), new Required(new Argument("N"), new Argument("M"))).Match(new Pattern[] { new Argument(null, 1), new Argument(null, 2) }) 60 | ); 61 | } 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /src/DocoptNet/Tokens.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace DocoptNet 8 | { 9 | public class Tokens: IEnumerable 10 | { 11 | private readonly Type _errorType; 12 | private readonly List _tokens = new List(); 13 | 14 | public Tokens(IEnumerable source, Type errorType) 15 | { 16 | _errorType = errorType ?? typeof(DocoptInputErrorException); 17 | _tokens.AddRange(source); 18 | } 19 | 20 | public Tokens(string source, Type errorType) 21 | { 22 | _errorType = errorType ?? typeof(DocoptInputErrorException); 23 | _tokens.AddRange(source.Split(new char[0], StringSplitOptions.RemoveEmptyEntries)); 24 | } 25 | 26 | public Type ErrorType 27 | { 28 | get { return _errorType; } 29 | } 30 | 31 | public bool ThrowsInputError 32 | { 33 | get { return ErrorType == typeof (DocoptInputErrorException); } 34 | } 35 | 36 | 37 | public static Tokens FromPattern(string pattern) 38 | { 39 | var spacedOut = Regex.Replace(pattern, @"([\[\]\(\)\|]|\.\.\.)", @" $1 "); 40 | var source = Regex.Split(spacedOut, @"\s+|(\S*<.*?>)").Where(x => !string.IsNullOrEmpty(x)); 41 | return new Tokens(source, typeof(DocoptLanguageErrorException)); 42 | } 43 | 44 | public IEnumerator GetEnumerator() 45 | { 46 | return _tokens.GetEnumerator(); 47 | } 48 | 49 | IEnumerator IEnumerable.GetEnumerator() 50 | { 51 | return GetEnumerator(); 52 | } 53 | 54 | public string Move() 55 | { 56 | string s = null; 57 | if (_tokens.Count > 0) 58 | { 59 | s = _tokens[0]; 60 | _tokens.RemoveAt(0); 61 | } 62 | return s; 63 | } 64 | 65 | public string Current() 66 | { 67 | return (_tokens.Count > 0) ? _tokens[0] : null; 68 | } 69 | 70 | public Exception CreateException(string message) 71 | { 72 | return Activator.CreateInstance(_errorType, new object[] {message}) as Exception; 73 | } 74 | 75 | public override string ToString() 76 | { 77 | return string.Format("current={0},count={1}", Current(), _tokens.Count); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/OptionParseTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class OptionParseTests 7 | { 8 | [Test] 9 | public void Short_and_long() 10 | { 11 | Assert.AreEqual(new Option("-h", null), Option.Parse("-h")); 12 | Assert.AreEqual(new Option(null, "--help"), Option.Parse("--help")); 13 | Assert.AreEqual(new Option("-h", "--help"), Option.Parse("-h --help")); 14 | Assert.AreEqual(new Option("-h", "--help"), Option.Parse("-h, --help")); 15 | } 16 | 17 | [Test] 18 | public void With_args() 19 | { 20 | Assert.AreEqual(new Option("-h", null, 1), Option.Parse("-h TOPIC")); 21 | Assert.AreEqual(new Option(null, "--help", 1), Option.Parse("--help TOPIC")); 22 | Assert.AreEqual(new Option("-h", "--help", 1), Option.Parse("-h TOPIC --help TOPIC")); 23 | Assert.AreEqual(new Option("-h", "--help", 1), Option.Parse("-h TOPIC, --help TOPIC")); 24 | Assert.AreEqual(new Option("-h", "--help", 1), Option.Parse("-h TOPIC, --help=TOPIC")); 25 | 26 | } 27 | 28 | [Test] 29 | public void With_description() 30 | { 31 | Assert.AreEqual(new Option("-h", null), Option.Parse("-h Description...")); 32 | Assert.AreEqual(new Option("-h", "--help"), Option.Parse("-h --help Description...")); 33 | Assert.AreEqual(new Option("-h", null, 1), Option.Parse("-h TOPIC Description...")); 34 | } 35 | 36 | [Test] 37 | public void Leading_spaces() 38 | { 39 | 40 | Assert.AreEqual(new Option("-h", null), Option.Parse(" -h")); 41 | } 42 | 43 | [Test] 44 | public void With_defaults() 45 | { 46 | 47 | Assert.AreEqual(new Option("-h", null, 1, "2"), Option.Parse("-h TOPIC Description... [default: 2]")); 48 | Assert.AreEqual(new Option("-h", null, 1, "2"), Option.Parse("-h TOPIC Description... [dEfAuLt: 2]")); 49 | } 50 | 51 | [Test] 52 | public void With_args_and_defaults() 53 | { 54 | 55 | Assert.AreEqual(new Option("-h", null, 1, "topic-1"), 56 | Option.Parse("-h TOPIC Description... [default: topic-1]")); 57 | Assert.AreEqual(new Option(null, "--help", 1, "3.14"), Option.Parse("--help=TOPIC ... [default: 3.14]")); 58 | Assert.AreEqual(new Option("-h", "--help", 1, "./"), Option.Parse("-h, --help=DIR ... [default: ./]")); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/PatterEitherTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class PatterEitherTests 7 | { 8 | [Test] 9 | public void Should_transform_option() 10 | { 11 | Assert.AreEqual( 12 | new Either(new Required(new Option("-a"))), 13 | Pattern.Transform(new Option("-a")) 14 | ); 15 | } 16 | 17 | [Test] 18 | public void Should_transform_arg() 19 | { 20 | Assert.AreEqual( 21 | new Either(new Required(new Argument("A"))), 22 | Pattern.Transform(new Argument("A")) 23 | ); 24 | } 25 | 26 | [Test] 27 | public void Should_distribute_opt() 28 | { 29 | Assert.AreEqual( 30 | new Either( 31 | new Required(new Option("-a"), new Option("-c")), 32 | new Required(new Option("-b"), new Option("-c"))), 33 | Pattern.Transform(new Required( 34 | new Either(new Option("-a"), new Option("-b")), new Option("-c"))) 35 | ); 36 | } 37 | 38 | [Test] 39 | public void Should_distribute_optional() 40 | { 41 | Assert.AreEqual( 42 | new Either( 43 | new Required(new Option("-b"), new Option("-a")), 44 | new Required(new Option("-c"), new Option("-a"))), 45 | Pattern.Transform(new Optional( 46 | new Option("-a"), new Either(new Option("-b"), new Option("-c")))) 47 | ); 48 | } 49 | 50 | [Test] 51 | public void Should_split_either() 52 | { 53 | Assert.AreEqual( 54 | new Either( 55 | new Required(new Option("-x")), 56 | new Required(new Option("-y")), 57 | new Required(new Option("-z")) 58 | ), 59 | Pattern.Transform(new Either(new Option("-x"), 60 | new Either(new Option("-y"), new Option("-z")))) 61 | ); 62 | } 63 | 64 | [Test] 65 | public void Should_transform_oneormore_args() 66 | { 67 | Assert.AreEqual( 68 | new Either(new Required(new Argument("N"), new Argument("M"), 69 | new Argument("N"), new Argument("M"))), 70 | Pattern.Transform(new OneOrMore(new Argument("N"), new Argument("M"))) 71 | ); 72 | } 73 | } 74 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/GenerateCodeTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using NUnit.Framework; 4 | 5 | namespace DocoptNet.Tests 6 | { 7 | [TestFixture] 8 | public class GenerateCodeTests 9 | { 10 | [Test] 11 | public void Should_generate_code() 12 | { 13 | // TODO: not a very useful test, more of a sanity check 14 | 15 | const string USAGE = @"Test host app for T4 Docopt.NET 16 | 17 | Usage: 18 | prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch-dash] 19 | prog command2 ARG 20 | prog files FILE... 21 | 22 | Options: 23 | -o Short switch. 24 | -s= Short option with arg. [default: 128] 25 | --long=ARG Long option with arg. 26 | --switch-dash Long switch. 27 | 28 | Explanation: 29 | This is a test usage file. 30 | "; 31 | const string expected = @" 32 | public bool CmdCommand { get { return _args[""command""].IsTrue; } } 33 | public string ArgArg { get { return null == _args[""ARG""] ? null : _args[""ARG""].ToString(); } } 34 | public string ArgMyarg { get { return null == _args[""""] ? null : _args[""""].ToString(); } } 35 | public string ArgOptionalarg { get { return null == _args[""OPTIONALARG""] ? null : _args[""OPTIONALARG""].ToString(); } } 36 | public bool OptO { get { return _args[""-o""].IsTrue; } } 37 | public string OptS { get { return null == _args[""-s""] ? ""128"" : _args[""-s""].ToString(); } } 38 | public string OptLong { get { return null == _args[""--long""] ? null : _args[""--long""].ToString(); } } 39 | public bool OptSwitchDash { get { return _args[""--switch-dash""].IsTrue; } } 40 | public bool CmdCommand2 { get { return _args[""command2""].IsTrue; } } 41 | public bool CmdFiles { get { return _args[""files""].IsTrue; } } 42 | public ArrayList ArgFile { get { return _args[""FILE""].AsList; } } 43 | "; 44 | var s = new Docopt().GenerateCode(USAGE); 45 | Assert.AreEqual(NormalizeNewLines(expected), NormalizeNewLines(s)); 46 | } 47 | 48 | private static string NormalizeNewLines(string s) 49 | { 50 | return string.Join(Environment.NewLine, s.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)); 51 | } 52 | 53 | [Test] 54 | public void Short_and_long_option_lead_to_only_one_property() 55 | { 56 | const string usage = @"Usage: 57 | naval_fate -h | --help 58 | 59 | Options: 60 | -h --help Show this screen."; 61 | 62 | var generatedCode = new Docopt().GenerateCode(usage); 63 | 64 | Assert.AreEqual(1, Regex.Matches(generatedCode, "OptHelp").Count); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /src/DocoptNet/Node.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace DocoptNet 7 | { 8 | public enum ValueType { Bool, List, String, } 9 | 10 | public class ArgumentNode : Node 11 | { 12 | public ArgumentNode(string name, ValueType valueType) : base(name, valueType) { } 13 | } 14 | 15 | public class OptionNode : Node 16 | { 17 | public OptionNode(string name, ValueType valueType) : base(name, valueType) { } 18 | } 19 | 20 | public class CommandNode : Node 21 | { 22 | public CommandNode(string name) : base(name, ValueType.Bool) { } 23 | } 24 | 25 | public abstract class Node : IEquatable 26 | { 27 | private class EmptyNode : Node 28 | { 29 | public EmptyNode() : base("", (ValueType)0) { } 30 | } 31 | 32 | /// 33 | /// Indicates an empty or non-existant node. 34 | /// 35 | public static readonly Node Empty = new EmptyNode(); 36 | 37 | protected Node(string name, ValueType valueType) 38 | { 39 | if (name == null) throw new ArgumentNullException("name"); 40 | 41 | this.Name = name; 42 | this.ValueType = valueType; 43 | } 44 | 45 | public ValueType ValueType { get; private set; } 46 | public string Name { get; private set; } 47 | 48 | public override string ToString() 49 | { 50 | return string.Format("{0} {1} {2}", GetType().Name, Name, ValueType); 51 | } 52 | 53 | public override int GetHashCode() 54 | { 55 | return this.Name.GetHashCode() ^ this.ValueType.GetHashCode(); 56 | } 57 | 58 | public bool Equals(Node other) 59 | { 60 | if (object.ReferenceEquals(null, other)) 61 | { 62 | return false; 63 | } 64 | 65 | if (object.ReferenceEquals(this, other)) 66 | { 67 | return true; 68 | } 69 | 70 | return other.Name == this.Name 71 | && other.ValueType == this.ValueType; 72 | } 73 | 74 | public override bool Equals(object obj) 75 | { 76 | if (object.ReferenceEquals(null, obj)) 77 | { 78 | return false; 79 | } 80 | 81 | if (object.ReferenceEquals(this, obj)) 82 | { 83 | return true; 84 | } 85 | 86 | if (obj.GetType() != this.GetType()) 87 | { 88 | return false; 89 | } 90 | 91 | return this.Equals((Node)obj); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/CommandsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace DocoptNet.Tests 5 | { 6 | [TestFixture] 7 | public class CommandsTests 8 | { 9 | [Test] 10 | public void Required() 11 | { 12 | var expected = new Dictionary 13 | { 14 | {"add", new ValueObject(true)} 15 | }; 16 | var actual = new Docopt().Apply("Usage: prog add", "add"); 17 | Assert.AreEqual(expected, actual); 18 | } 19 | 20 | [Test] 21 | public void Optional_no_args() 22 | { 23 | var expected = new Dictionary 24 | { 25 | {"add", new ValueObject(false)} 26 | }; 27 | var actual = new Docopt().Apply("Usage: prog [add]", ""); 28 | Assert.AreEqual(expected, actual); 29 | } 30 | 31 | [Test] 32 | public void Optional_one_arg() 33 | { 34 | var expected = new Dictionary 35 | { 36 | {"add", new ValueObject(true)} 37 | }; 38 | var actual = new Docopt().Apply("Usage: prog [add]", "add"); 39 | Assert.AreEqual(expected, actual); 40 | } 41 | 42 | [Test] 43 | public void Optional_either_first_specified() 44 | { 45 | var expected = new Dictionary 46 | { 47 | {"add", new ValueObject(true)}, 48 | {"rm", new ValueObject(false)} 49 | }; 50 | var actual = new Docopt().Apply("Usage: prog (add|rm)", "add"); 51 | Assert.AreEqual(expected, actual); 52 | } 53 | 54 | [Test] 55 | public void Optional_either_second_specified() 56 | { 57 | var expected = new Dictionary 58 | { 59 | {"add", new ValueObject(false)}, 60 | {"rm", new ValueObject(true)} 61 | }; 62 | var actual = new Docopt().Apply("Usage: prog (add|rm)", "rm"); 63 | Assert.AreEqual(expected, actual); 64 | } 65 | 66 | [Test] 67 | public void Required_both_specified() 68 | { 69 | var expected = new Dictionary 70 | { 71 | {"a", new ValueObject(true)}, 72 | {"b", new ValueObject(true)} 73 | }; 74 | var actual = new Docopt().Apply("Usage: prog a b", "a b"); 75 | Assert.AreEqual(expected, actual); 76 | } 77 | 78 | [Test] 79 | public void Required_wrong_order() 80 | { 81 | Assert.Throws(() => new Docopt().Apply("Usage: prog a b", "b a")); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /src/Examples/NavalFate/NavalFate.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2B454F8B-D702-4281-80DF-367142007B0C} 8 | Exe 9 | Properties 10 | NavalFate 11 | NavalFate 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | {d87f19b8-a15f-4bc2-8e1c-47cc6038a0df} 53 | DocoptNet 54 | 55 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/SyntaxTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace DocoptNet.Tests 5 | { 6 | [TestFixture] 7 | public class SyntaxTests 8 | { 9 | [Test] 10 | public void Missing_closing_square_bracket() 11 | { 12 | Assert.Throws( 13 | () => new Docopt().Apply("Usage: prog [a [b]")); 14 | 15 | } 16 | 17 | [Test] 18 | public void Missing_opening_paren() 19 | { 20 | Assert.Throws( 21 | () => new Docopt().Apply("Usage: prog [a [b] ] c )")); 22 | 23 | } 24 | 25 | [Test] 26 | public void Detect_double_dash() 27 | { 28 | var expected = new Dictionary 29 | { 30 | {"-o", new ValueObject(false)}, 31 | {"", new ValueObject("-o")}, 32 | {"--", new ValueObject(true)} 33 | }; 34 | var actual = new Docopt().Apply("usage: prog [-o] [--] \nOptions: -o", "-- -o"); 35 | Assert.AreEqual(expected, actual); 36 | } 37 | 38 | [Test] 39 | public void No_double_dash() 40 | { 41 | var expected = new Dictionary 42 | { 43 | {"-o", new ValueObject(true)}, 44 | {"", new ValueObject("1")}, 45 | {"--", new ValueObject(false)} 46 | }; 47 | var actual = new Docopt().Apply("usage: prog [-o] [--] \nOptions: -o", "-o 1"); 48 | Assert.AreEqual(expected, actual); 49 | } 50 | 51 | [Test] 52 | public void Double_dash_not_allowed() // FIXME? 53 | { 54 | Assert.Throws( 55 | () => new Docopt().Apply("usage: prog [-o] \noptions:-o", "-- -o")); 56 | 57 | } 58 | 59 | [Test] 60 | public void No_usage() 61 | { 62 | Assert.Throws( 63 | () => new Docopt().Apply("no usage with colon here")); 64 | 65 | } 66 | 67 | [Test] 68 | public void Duplicate_usage() 69 | { 70 | Assert.Throws( 71 | () => new Docopt().Apply("usage: here \n\n and again usage: here")); 72 | 73 | } 74 | 75 | [Test] 76 | public void Test_issue_71_double_dash_is_not_a_valid_option_argument() 77 | { 78 | Assert.Throws( 79 | () => new Docopt().Apply("usage: prog [--log=LEVEL] [--] ...", "--log -- 1 2")); 80 | 81 | Assert.Throws( 82 | () => new Docopt().Apply("usage: prog [-l LEVEL] [--] ...\r\n" + 83 | "options: -l LEVEL", "-l -- 1 2")); 84 | } 85 | 86 | } 87 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/CountMultipleFlagsTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using NUnit.Framework; 3 | 4 | namespace DocoptNet.Tests 5 | { 6 | [TestFixture] 7 | public class CountMultipleFlagsTests 8 | { 9 | [Test] 10 | public void Simple_flag() 11 | { 12 | var expected = new Dictionary 13 | { 14 | {"-v", new ValueObject(true)} 15 | }; 16 | var actual = new Docopt().Apply("Usage: prog [-v]", "-v"); 17 | Assert.AreEqual(expected, actual); 18 | } 19 | 20 | [Test] 21 | public void Flag_0() 22 | { 23 | var expected = new Dictionary 24 | { 25 | {"-v", new ValueObject(0)} 26 | }; 27 | var actual = new Docopt().Apply("Usage: prog [-vv]", ""); 28 | Assert.AreEqual(expected, actual); 29 | } 30 | 31 | [Test] 32 | public void Flag_1() 33 | { 34 | var expected = new Dictionary 35 | { 36 | {"-v", new ValueObject(1)} 37 | }; 38 | var actual = new Docopt().Apply("Usage: prog [-vv]", "-v"); 39 | Assert.AreEqual(expected, actual); 40 | } 41 | 42 | [Test] 43 | public void Flag_2() 44 | { 45 | var expected = new Dictionary 46 | { 47 | {"-v", new ValueObject(2)} 48 | }; 49 | var actual = new Docopt().Apply("Usage: prog [-vv]", "-vv"); 50 | Assert.AreEqual(expected, actual); 51 | } 52 | 53 | [Test] 54 | public void Flag_too_many() 55 | { 56 | Assert.Throws(() => new Docopt().Apply("Usage: prog [-vv]", "-vvv")); 57 | } 58 | 59 | [Test] 60 | public void Flag_3() 61 | { 62 | var expected = new Dictionary 63 | { 64 | {"-v", new ValueObject(3)} 65 | }; 66 | var actual = new Docopt().Apply("Usage: prog [-v | -vv | -vvv]", "-vvv"); 67 | Assert.AreEqual(expected, actual); 68 | } 69 | 70 | [Test] 71 | public void Flag_one_or_more() 72 | { 73 | var expected = new Dictionary 74 | { 75 | {"-v", new ValueObject(6)} 76 | }; 77 | var actual = new Docopt().Apply("Usage: prog -v...", "-vvvvvv"); 78 | Assert.AreEqual(expected, actual); 79 | } 80 | 81 | [Test] 82 | public void Flag_long_2() 83 | { 84 | var expected = new Dictionary 85 | { 86 | {"--ver", new ValueObject(2)} 87 | }; 88 | var actual = new Docopt().Apply("Usage: prog [--ver --ver]", "--ver --ver"); 89 | Assert.AreEqual(expected, actual); 90 | } 91 | 92 | } 93 | } -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/T4DocoptNet.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using DocoptNet; 5 | 6 | namespace T4DocoptNetHostApp 7 | { 8 | // Generated class for Another.usage.txt 9 | public class AnotherArgs 10 | { 11 | public const string USAGE = @"Test host app for T4 Docopt.NET 12 | 13 | Usage: 14 | other command1 ARG [-o --long=ARG --switch -v] 15 | other command2 16 | 17 | Options: 18 | -o A string with some double ""quotes"". 19 | "; 20 | private readonly IDictionary _args; 21 | public AnotherArgs(ICollection argv, bool help = true, 22 | object version = null, bool optionsFirst = false, bool exit = false) 23 | { 24 | _args = new Docopt().Apply(USAGE, argv, help, version, optionsFirst, exit); 25 | } 26 | 27 | public IDictionary Args 28 | { 29 | get { return _args; } 30 | } 31 | 32 | public bool CmdCommand1 { get { return _args["command1"].IsTrue; } } 33 | public string ArgArg { get { return _args["ARG"].ToString(); } } 34 | public bool OptO { get { return _args["-o"].IsTrue; } } 35 | public string OptLong { get { return _args["--long"].ToString(); } } 36 | public bool OptSwitch { get { return _args["--switch"].IsTrue; } } 37 | public bool OptV { get { return _args["-v"].IsTrue; } } 38 | public bool CmdCommand2 { get { return _args["command2"].IsTrue; } } 39 | 40 | } 41 | 42 | // Generated class for Main.usage.txt 43 | public class MainArgs 44 | { 45 | public const string USAGE = @"Test host app for T4 Docopt.NET 46 | 47 | Usage: 48 | prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch] 49 | prog files FILE... 50 | 51 | Options: 52 | -o Short switch. 53 | -s= Short option with arg. 54 | --long=ARG Long option with arg. 55 | --switch Long switch. 56 | 57 | Explanation: 58 | This is a test usage file. 59 | "; 60 | private readonly IDictionary _args; 61 | public MainArgs(ICollection argv, bool help = true, 62 | object version = null, bool optionsFirst = false, bool exit = false) 63 | { 64 | _args = new Docopt().Apply(USAGE, argv, help, version, optionsFirst, exit); 65 | } 66 | 67 | public IDictionary Args 68 | { 69 | get { return _args; } 70 | } 71 | 72 | public bool CmdCommand { get { return _args["command"].IsTrue; } } 73 | public string ArgArg { get { return _args["ARG"].ToString(); } } 74 | public string ArgMyarg { get { return _args[""].ToString(); } } 75 | public string ArgOptionalarg { get { return _args["OPTIONALARG"].ToString(); } } 76 | public bool OptO { get { return _args["-o"].IsTrue; } } 77 | public string OptS { get { return _args["-s"].ToString(); } } 78 | public string OptLong { get { return _args["--long"].ToString(); } } 79 | public bool OptSwitch { get { return _args["--switch"].IsTrue; } } 80 | public bool CmdFiles { get { return _args["files"].IsTrue; } } 81 | public ArrayList ArgFile { get { return _args["FILE"].AsList; } } 82 | 83 | } 84 | 85 | 86 | } 87 | 88 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/OptionalMatchTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class OptionalMatchTests 7 | { 8 | [Test] 9 | public void Should_match_option() 10 | { 11 | Assert.AreEqual( 12 | new MatchResult(true, 13 | new Pattern[0], 14 | new Pattern[] {new Option("-a")}), 15 | new Optional(new Option("-a")).Match(new Pattern[] {new Option("-a")}) 16 | ); 17 | } 18 | 19 | [Test] 20 | public void Should_match_empty() 21 | { 22 | Assert.AreEqual( 23 | new MatchResult(true, 24 | new Pattern[0], 25 | new Pattern[0]), 26 | new Optional(new Option("-a")).Match(new Pattern[0]) 27 | ); 28 | } 29 | 30 | [Test] 31 | public void Should_not_collect_other_opt() 32 | { 33 | Assert.AreEqual( 34 | new MatchResult(true, 35 | new Pattern[] {new Option("-x")}, 36 | new Pattern[0] 37 | ), 38 | new Optional(new Option("-a")).Match(new Pattern[] {new Option("-x")}) 39 | ); 40 | } 41 | 42 | [Test] 43 | public void Should_match_first_option() 44 | { 45 | Assert.AreEqual( 46 | new MatchResult(true, 47 | new Pattern[0], 48 | new Pattern[] {new Option("-a")}), 49 | new Optional(new Option("-a"), new Option("-b")).Match(new Pattern[] {new Option("-a")}) 50 | ); 51 | } 52 | 53 | [Test] 54 | public void Should_not_collect_other_option_2() 55 | { 56 | Assert.AreEqual( 57 | new MatchResult(true, 58 | new Pattern[] {new Option("-x")}, 59 | new Pattern[0]), 60 | new Optional(new Option("-a"), new Option("-b")).Match(new Pattern[] {new Option("-x")}) 61 | ); 62 | } 63 | 64 | [Test] 65 | public void Should_match_optional_arg() 66 | { 67 | Assert.AreEqual( 68 | new MatchResult(true, 69 | new Pattern[0], 70 | new Pattern[] { new Argument("N", new ValueObject(9)) }), 71 | new Optional(new Argument("N")).Match(new Pattern[] { new Argument(null, new ValueObject(9)) }) 72 | ); 73 | } 74 | 75 | [Test] 76 | public void Should_collect_all_except_other() 77 | { 78 | Assert.AreEqual( 79 | new MatchResult(true, 80 | new Pattern[] { new Option("-x") }, 81 | new Pattern[] { new Option("-a"), new Option("-b") }), 82 | new Optional(new Option("-a"), new Option("-b")).Match( 83 | new Pattern[] { new Option("-b"), new Option("-x"), new Option("-a") }) 84 | ); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/ParseArgvTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class ParseArgvTests 7 | { 8 | private readonly Option[] _options = new[] 9 | {new Option("-h"), new Option("-v", "--verbose"), new Option("-f", "--file", 1)}; 10 | 11 | private Tokens TS(string s) 12 | { 13 | return new Tokens(s, typeof(DocoptInputErrorException)); 14 | } 15 | 16 | [Test] 17 | public void Test_parse_argv_empty() 18 | { 19 | Assert.IsEmpty(Docopt.ParseArgv(TS(""), _options)); 20 | } 21 | 22 | [Test] 23 | public void Test_parse_argv_one_opt() 24 | { 25 | Assert.AreEqual(new[] {new Option("-h", null, 0, new ValueObject(true))}, 26 | Docopt.ParseArgv(TS("-h"), _options)); 27 | } 28 | 29 | [Test] 30 | public void Test_parse_argv_short_and_long() 31 | { 32 | Assert.AreEqual(new[] 33 | { 34 | new Option("-h", null, 0, new ValueObject(true)), 35 | new Option("-v", "--verbose", 0, new ValueObject(true)) 36 | }, 37 | Docopt.ParseArgv(TS("-h --verbose"), _options)); 38 | } 39 | 40 | [Test] 41 | public void Test_parse_argv_opt_with_arg() 42 | { 43 | Assert.AreEqual(new[] 44 | { 45 | new Option("-h", null, 0, new ValueObject(true)), 46 | new Option("-f", "--file", 1, "f.txt") 47 | }, 48 | Docopt.ParseArgv(TS("-h --file f.txt"), _options)); 49 | } 50 | 51 | [Test] 52 | public void Test_parse_argv_with_arg() 53 | { 54 | Assert.AreEqual( 55 | new Pattern[] 56 | { 57 | new Option("-h", null, 0, new ValueObject(true)), 58 | new Option("-f", "--file", 1, "f.txt"), 59 | new Argument(null, "arg") 60 | }, 61 | Docopt.ParseArgv(TS("-h --file f.txt arg"), _options)); 62 | } 63 | 64 | [Test] 65 | public void Test_parse_argv_two_args() 66 | { 67 | Assert.AreEqual( 68 | new Pattern[] 69 | { 70 | new Option("-h", null, 0, new ValueObject(true)), 71 | new Option("-f", "--file", 1, "f.txt"), 72 | new Argument(null, "arg"), 73 | new Argument(null, "arg2") 74 | }, 75 | Docopt.ParseArgv(TS("-h --file f.txt arg arg2"), _options)); 76 | } 77 | 78 | [Test] 79 | public void Test_parse_argv_with_double_dash() 80 | { 81 | Assert.AreEqual( 82 | new Pattern[] 83 | { 84 | new Option("-h", null, 0, new ValueObject(true)), 85 | new Argument(null, "arg"), 86 | new Argument(null, "--"), 87 | new Argument(null, "-v") 88 | }, 89 | Docopt.ParseArgv(TS("-h arg -- -v"), _options)); 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /.paket/paket.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | 7 | true 8 | $(MSBuildThisFileDirectory) 9 | $(MSBuildThisFileDirectory)..\ 10 | /Library/Frameworks/Mono.framework/Commands/mono 11 | mono 12 | 13 | 14 | 15 | $(PaketToolsPath)paket.exe 16 | $(PaketToolsPath)paket.bootstrapper.exe 17 | "$(PaketExePath)" 18 | $(MonoPath) --runtime=v4.0.30319 "$(PaketExePath)" 19 | "$(PaketBootStrapperExePath)" $(PaketBootStrapperCommandArgs) 20 | $(MonoPath) --runtime=v4.0.30319 $(PaketBootStrapperExePath) $(PaketBootStrapperCommandArgs) 21 | 22 | $(MSBuildProjectDirectory)\paket.references 23 | $(MSBuildStartupDirectory)\paket.references 24 | $(MSBuildProjectFullPath).paket.references 25 | $(PaketCommand) restore --references-files "$(PaketReferences)" 26 | $(PaketBootStrapperCommand) 27 | 28 | RestorePackages; $(BuildDependsOn); 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/DocoptNet/LeafPattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace DocoptNet 6 | { 7 | /// 8 | /// Leaf/terminal node of a pattern tree. 9 | /// 10 | internal class LeafPattern: Pattern 11 | { 12 | private readonly string _name; 13 | 14 | protected LeafPattern(string name, ValueObject value=null) 15 | { 16 | _name = name; 17 | Value = value; 18 | } 19 | 20 | protected LeafPattern() 21 | { 22 | } 23 | 24 | public override string Name 25 | { 26 | get { return _name; } 27 | } 28 | 29 | public override ICollection Flat(params Type[] types) 30 | { 31 | if (types == null) throw new ArgumentNullException("types"); 32 | if (types.Length == 0 || types.Contains(this.GetType())) 33 | { 34 | return new Pattern[] { this }; 35 | } 36 | return new Pattern[] {}; 37 | } 38 | 39 | public virtual SingleMatchResult SingleMatch(IList patterns) 40 | { 41 | return new SingleMatchResult(); 42 | } 43 | 44 | public override MatchResult Match(IList left, IEnumerable collected = null) 45 | { 46 | var coll = collected ?? new List(); 47 | var sresult = SingleMatch(left); 48 | var match = sresult.Match; 49 | if (match == null) 50 | { 51 | return new MatchResult(false, left, coll); 52 | } 53 | var left_ = new List(); 54 | left_.AddRange(left.Take(sresult.Position)); 55 | left_.AddRange(left.Skip(sresult.Position + 1)); 56 | var sameName = coll.Where(a => a.Name == Name).ToList(); 57 | if (Value != null && (Value.IsList || Value.IsOfTypeInt)) 58 | { 59 | var increment = new ValueObject(1); 60 | if (!Value.IsOfTypeInt) 61 | { 62 | increment = match.Value.IsString ? new ValueObject(new [] {match.Value}) : match.Value; 63 | } 64 | if (sameName.Count == 0) 65 | { 66 | match.Value = increment; 67 | var res = new List(coll) {match}; 68 | return new MatchResult(true, left_, res); 69 | } 70 | sameName[0].Value.Add(increment); 71 | return new MatchResult(true, left_, coll); 72 | } 73 | var resColl = new List(); 74 | resColl.AddRange(coll); 75 | resColl.Add(match); 76 | return new MatchResult(true, left_, resColl); 77 | } 78 | 79 | public override string ToString() 80 | { 81 | return string.Format("{0}({1}, {2})", GetType().Name, Name, Value); 82 | } 83 | } 84 | 85 | internal class SingleMatchResult 86 | { 87 | public SingleMatchResult(int index, Pattern match) 88 | { 89 | Position = index; 90 | Match = match; 91 | } 92 | 93 | public SingleMatchResult() 94 | { 95 | } 96 | 97 | public int Position { get; set; } 98 | public Pattern Match { get; set; } 99 | } 100 | } -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/T4DocoptNet.tt: -------------------------------------------------------------------------------- 1 | <# 2 | /* 3 | T4DocopNet Version 0.1.0 4 | */ 5 | #> 6 | <#@ template language="C#" debug="true" hostspecific="true" #> 7 | <#@ assembly name="System.Core" #> 8 | <#@ assembly name="Microsoft.VisualStudio.Shell.Interop" #> 9 | <#@ assembly name="EnvDTE" #> 10 | <#@ assembly name="EnvDTE80" #> 11 | <#@ assembly name="VSLangProj" #> 12 | <#@ assembly name="System.Xml" #> 13 | <#@ assembly name="System.Xml.Linq" #> 14 | <# 15 | // This directive will be updated at package install time 16 | // by install.ps1. It can also be set manually to point 17 | // to the right path. 18 | #> 19 | <#@ assembly name="$(ProjectDir)$(OutDir)DocoptNet.dll" #> 20 | <#@ import namespace="System.Collections.Generic" #> 21 | <#@ import namespace="System.IO" #> 22 | <#@ import namespace="System.Linq" #> 23 | <#@ import namespace="DocoptNet" #> 24 | <#@ import namespace="System.Text" #> 25 | <#@ import namespace="System.Text.RegularExpressions" #> 26 | <#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #> 27 | <#@ import namespace="EnvDTE" #> 28 | <#@ import namespace="EnvDTE80" #> 29 | <#@ import namespace="Microsoft.VisualStudio.TextTemplating" #> 30 | 31 | using System.Collections; 32 | using System.Collections.Generic; 33 | using DocoptNet; 34 | 35 | namespace <#= System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint") #> 36 | { 37 | <# 38 | var templateDir = Path.GetDirectoryName(Host.TemplateFile); 39 | var files = Directory.EnumerateFiles(templateDir).Where(f => f.EndsWith(".usage.txt")); 40 | foreach (var file in files) 41 | { 42 | var entry = new UsageFileEntry(file); 43 | #> 44 | // Generated class for <#= Path.GetFileName(entry.FileName) #> 45 | public class <#= entry.Name #>Args 46 | { 47 | public const string USAGE = @"<#= entry.Usage #>"; 48 | private readonly IDictionary _args; 49 | public <#= entry.Name #>Args(ICollection argv, bool help = true, 50 | object version = null, bool optionsFirst = false, bool exit = false) 51 | { 52 | _args = new Docopt().Apply(USAGE, argv, help, version, optionsFirst, exit); 53 | } 54 | 55 | public IDictionary Args 56 | { 57 | get { return _args; } 58 | } 59 | 60 | <# 61 | var s = new Docopt().GenerateCode(entry.Usage); 62 | PushIndent(" "); 63 | Write(s); 64 | ClearIndent(); 65 | #> 66 | } 67 | 68 | <# 69 | } 70 | 71 | #> 72 | } 73 | 74 | <#+ 75 | class UsageFileEntry 76 | { 77 | public string FileName; 78 | public string Name; 79 | public string Usage; 80 | public UsageFileEntry(string fileName) 81 | { 82 | FileName = fileName; 83 | Name = ExtractName(fileName); 84 | Usage = File.ReadAllText(fileName).Replace("\"", "\"\""); 85 | } 86 | 87 | private static string ExtractName(string fileName) 88 | { 89 | 90 | var s = Path.GetFileName(fileName).Replace(".usage.txt", ""); 91 | var res = ""; 92 | var first = true; 93 | foreach (char c in s) 94 | { 95 | if (char.IsLetterOrDigit(c)) 96 | { 97 | res += first ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c); 98 | first = false; 99 | } 100 | else 101 | { 102 | res += "_"; 103 | } 104 | } 105 | return res; 106 | } 107 | } 108 | 109 | /* IMPORTANT: Do not add blanks after this last tag as it would break the T4 generation 110 | * See http://stackoverflow.com/questions/11379471/error-a-template-containing-a-class-feature-must-end-with-a-class-feature 111 | */ 112 | #> -------------------------------------------------------------------------------- /src/T4DocoptNetHostApp/T4DocoptNetHostApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {5A641DFB-CB1A-4044-AF52-554CC8904449} 8 | Exe 9 | Properties 10 | T4DocoptNetHostApp 11 | T4DocoptNetHostApp 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | True 48 | True 49 | T4DocoptNet.tt 50 | 51 | 52 | 53 | 54 | 55 | TextTemplatingFileGenerator 56 | T4DocoptNet.cs 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Designer 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | {d87f19b8-a15f-4bc2-8e1c-47cc6038a0df} 73 | DocoptNet 74 | 75 | 76 | 77 | 84 | -------------------------------------------------------------------------------- /src/DocoptNet/Option.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace DocoptNet 7 | { 8 | internal class Option : LeafPattern 9 | { 10 | public string ShortName { get; private set; } 11 | public string LongName { get; private set; } 12 | public int ArgCount { get; private set; } 13 | 14 | public Option(string shortName = null, string longName = null, int argCount = 0, ValueObject value = null) 15 | : base() 16 | { 17 | ShortName = shortName; 18 | LongName = longName; 19 | ArgCount = argCount; 20 | var v = value ?? new ValueObject(false); 21 | Value = (v.IsFalse && argCount > 0) ? null : v; 22 | } 23 | 24 | public Option(string shortName, string longName, int argCount, string value) 25 | : this(shortName, longName, argCount, new ValueObject(value)) 26 | { 27 | } 28 | 29 | public override string Name 30 | { 31 | get { return LongName ?? ShortName; } 32 | } 33 | 34 | public override Node ToNode() 35 | { 36 | return new OptionNode(this.Name.TrimStart('-'), this.ArgCount == 0 ? ValueType.Bool : ValueType.String); 37 | } 38 | 39 | public override string GenerateCode() 40 | { 41 | var s = Name.ToLowerInvariant(); 42 | s = "Opt" + GenerateCodeHelper.ConvertDashesToCamelCase(s); 43 | 44 | if (ArgCount == 0) 45 | { 46 | return string.Format("public bool {0} {{ get {{ return _args[\"{1}\"].IsTrue; }} }}", s, Name); 47 | } 48 | var defaultValue = Value == null ? "null" : string.Format("\"{0}\"", Value); 49 | return string.Format("public string {0} {{ get {{ return null == _args[\"{1}\"] ? {2} : _args[\"{1}\"].ToString(); }} }}", s, Name, defaultValue); 50 | } 51 | 52 | public override SingleMatchResult SingleMatch(IList left) 53 | { 54 | for (var i = 0; i < left.Count; i++) 55 | { 56 | if (left[i].Name == Name) 57 | return new SingleMatchResult(i, left[i]); 58 | } 59 | return new SingleMatchResult(); 60 | } 61 | 62 | public override string ToString() 63 | { 64 | return string.Format("Option({0},{1},{2},{3})", ShortName, LongName, ArgCount, Value); 65 | } 66 | 67 | private const string DESC_SEPARATOR = " "; 68 | 69 | public static Option Parse(string optionDescription) 70 | { 71 | if (optionDescription == null) throw new ArgumentNullException("optionDescription"); 72 | 73 | string shortName = null; 74 | string longName = null; 75 | var argCount = 0; 76 | var value = new ValueObject(false); 77 | var p = new StringPartition(optionDescription, DESC_SEPARATOR); 78 | var options = p.LeftString; 79 | var description = p.RightString; 80 | foreach (var s in options.Split(" \t,=".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) 81 | { 82 | if (s.StartsWith("--")) 83 | longName = s; 84 | else if (s.StartsWith("-")) 85 | { 86 | shortName = s; 87 | } 88 | else 89 | { 90 | argCount = 1; 91 | } 92 | } 93 | if (argCount > 0) 94 | { 95 | var r = new Regex(@"\[default: (.*)\]", RegexOptions.IgnoreCase); 96 | var m = r.Match(description); 97 | value = m.Success ? new ValueObject(m.Groups[1].Value) : null; 98 | } 99 | return new Option(shortName, longName, argCount, value); 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/GetNodesTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text.RegularExpressions; 3 | using NUnit.Framework; 4 | 5 | namespace DocoptNet.Tests 6 | { 7 | [TestFixture] 8 | public class GetNodesTests 9 | { 10 | [Test] 11 | public void Should_get_nodes() 12 | { 13 | const string USAGE = @"Test host app for T4 Docopt.NET 14 | 15 | Usage: 16 | prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch] 17 | prog files FILE... 18 | 19 | Options: 20 | -o --opt Short switch with a longer name. The longer name is the ""main"" one. 21 | -s= Short option with arg. 22 | --long=ARG Long option with arg. 23 | --switch Long switch. [default: false] 24 | 25 | Explanation: 26 | This is a test ""usage"". 27 | "; 28 | 29 | var actual = new Docopt().GetNodes(USAGE); 30 | 31 | CollectionAssert.AreEqual( 32 | new Node[] 33 | { 34 | new CommandNode ("command"), 35 | new ArgumentNode("ARG", ValueType.String), 36 | new ArgumentNode("", ValueType.String), 37 | new ArgumentNode("OPTIONALARG", ValueType.String), 38 | new OptionNode ("opt", ValueType.Bool), 39 | new OptionNode ("s", ValueType.String), 40 | new OptionNode ("long", ValueType.String), 41 | new OptionNode ("switch", ValueType.Bool), 42 | new CommandNode ("files"), 43 | new ArgumentNode("FILE", ValueType.List), 44 | }, 45 | actual); 46 | } 47 | 48 | [Test] 49 | public void Should_return_duplicates_so_caller_knows_which_args_are_after_each_command() 50 | { 51 | const string USAGE = @"Test for duplicate commands and arguments. 52 | Usage: 53 | prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch] 54 | prog command ARG [OPTIONALARG] [-o -s= --long=ARG --switch] FILE... 55 | prog diff-command [OPTIONALARG] ARG 56 | 57 | Options: 58 | -o --option Short & long option flag, the long will be used as it's more descriptive. 59 | -s= Short option with arg. 60 | --long=ARG Long option with arg. 61 | --switch Long switch. 62 | "; 63 | 64 | var actual = new Docopt().GetNodes(USAGE); 65 | 66 | CollectionAssert.AreEqual( 67 | new Node[] 68 | { 69 | new CommandNode ("command"), 70 | new ArgumentNode("ARG", ValueType.String), 71 | new ArgumentNode("", ValueType.String), 72 | new ArgumentNode("OPTIONALARG", ValueType.String), 73 | new OptionNode ("option", ValueType.Bool), 74 | new OptionNode ("s", ValueType.String), 75 | new OptionNode ("long", ValueType.String), 76 | new OptionNode ("switch", ValueType.Bool), 77 | new CommandNode ("command"), 78 | new ArgumentNode("ARG", ValueType.String), 79 | new ArgumentNode("", ValueType.String), 80 | new ArgumentNode("OPTIONALARG", ValueType.String), 81 | new OptionNode ("option", ValueType.Bool), 82 | new OptionNode ("s", ValueType.String), 83 | new OptionNode ("long", ValueType.String), 84 | new OptionNode ("switch", ValueType.Bool), 85 | new ArgumentNode("FILE", ValueType.List), 86 | new CommandNode ("diff-command"), 87 | new ArgumentNode("", ValueType.String), 88 | new ArgumentNode("OPTIONALARG", ValueType.String), 89 | new ArgumentNode("ARG", ValueType.String), 90 | }, 91 | actual); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/DocoptNet/DocoptNet.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net40;netstandard1.5;netstandard2.0 5 | DocoptNet 6 | 0.6.1.10 7 | DocoptNet.snk 8 | true 9 | true 10 | DocoptNet 11 | Copyright (c) 2013 Dinh Doan Van Bien, dinh@doanvanbien.com 12 | 1.6.0 13 | 1.6.0 14 | false 15 | false 16 | docopt.net 17 | https://github.com/docopt/docopt.net/blob/master/LICENSE-MIT 18 | https://github.com/docopt/docopt.net 19 | https://secure.gravatar.com/avatar/e82bc289285e348387313a00cfd84979?s=400&d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-user-420.png 20 | docopt.net, a beautiful command-line parser 21 | Dinh Doan Van Bien;Vladimir Keleshev 22 | docopt.net is the .net version of the docopt python beautiful command line parser. docopt.net helps you define an interface for your command-line app, and automatically generate a parser for it. docopt.net is based on conventions that have been used for decades in help messages and man pages for program interface description. Interface description in docopt.net is such a help message, but formalized. Check out http://docopt.org for a more detailed explanation. 23 | Quick example: 24 | var arguments = new Docopt().Apply("Usage: prog [-a] [-b] FILE", args); 25 | if (arguments["-a"].IsTrue) {{ ... }} 26 | 27 | 28 | - T4DocoptNet.tt assembly path fix. 29 | - Added support for .net core RC2 30 | 31 | Copyright (c) 2012-2014 Vladimir Keleshev, Dinh Doan Van Bien 32 | parser;command line argument;option library;syntax;shell;beautiful;posix;python;console;command-line;docopt 33 | 1.6.0 34 | 1.6.0 35 | 36 | 37 | 38 | 39 | content\Main.usage.txt 40 | true 41 | 42 | 43 | content\T4DocoptNet.tt 44 | true 45 | 46 | 47 | content\T4DocoptNet.tt.hooks.t4 48 | true 49 | 50 | 51 | content\T4DocoptNet.tt.settings.xml 52 | true 53 | 54 | 55 | tools\install.ps1 56 | true 57 | 58 | 59 | 60 | 61 | 62 | 4.3.0 63 | 64 | 65 | 66 | 67 | 68 | 4.3.0 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/DocoptNet/ValueObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | 5 | namespace DocoptNet 6 | { 7 | public class ValueObject 8 | { 9 | public object Value { get; private set; } 10 | 11 | internal ValueObject(object obj) 12 | { 13 | if (obj is ArrayList) 14 | { 15 | Value = new ArrayList(obj as ArrayList); 16 | return; 17 | } 18 | if (obj is ICollection) 19 | { 20 | Value = new ArrayList(obj as ICollection); 21 | return; 22 | } 23 | Value = obj; 24 | } 25 | 26 | internal ValueObject() 27 | { 28 | Value = null; 29 | } 30 | 31 | public bool IsNullOrEmpty 32 | { 33 | get { return Value == null || Value.ToString() == ""; } 34 | } 35 | 36 | public bool IsFalse 37 | { 38 | get { return (Value as bool?) == false; } 39 | } 40 | 41 | public bool IsTrue 42 | { 43 | get { return (Value as bool?) == true; } 44 | } 45 | 46 | public bool IsList 47 | { 48 | get { return Value is ArrayList; } 49 | } 50 | 51 | internal bool IsOfTypeInt 52 | { 53 | get { return Value is int?; } 54 | } 55 | 56 | public bool IsInt 57 | { 58 | get 59 | { 60 | int value; 61 | return Value != null && (Value is int || Int32.TryParse(Value.ToString(), out value)); 62 | } 63 | } 64 | 65 | public int AsInt 66 | { 67 | get { return IsList ? 0 : Convert.ToInt32(Value); } 68 | } 69 | 70 | public bool IsString 71 | { 72 | get { return Value is string; } 73 | } 74 | 75 | public override bool Equals(object obj) 76 | { 77 | // 78 | // See the full list of guidelines at 79 | // http://go.microsoft.com/fwlink/?LinkID=85237 80 | // and also the guidance for operator== at 81 | // http://go.microsoft.com/fwlink/?LinkId=85238 82 | // 83 | 84 | if (obj == null || GetType() != obj.GetType()) 85 | { 86 | return false; 87 | } 88 | 89 | var v = (obj as ValueObject).Value; 90 | if (Value == null && v == null) return true; 91 | if (Value == null || v == null) return false; 92 | if (IsList || (obj as ValueObject).IsList) 93 | return Value.ToString().Equals(v.ToString()); 94 | return Value.Equals(v); 95 | } 96 | 97 | public override int GetHashCode() 98 | { 99 | return ToString().GetHashCode(); 100 | } 101 | 102 | public override string ToString() 103 | { 104 | if (IsList) 105 | { 106 | var l = (from object v in AsList select v.ToString()).ToList(); 107 | return string.Format("[{0}]", String.Join(", ", l)); 108 | } 109 | return (Value ?? "").ToString(); 110 | } 111 | 112 | internal void Add(ValueObject increment) 113 | { 114 | if (increment == null) throw new ArgumentNullException("increment"); 115 | 116 | if (increment.Value == null) throw new InvalidOperationException("increment.Value is null"); 117 | 118 | if (Value == null) throw new InvalidOperationException("Value is null"); 119 | 120 | if (increment.IsOfTypeInt) 121 | { 122 | if (IsList) 123 | (Value as ArrayList).Add(increment.AsInt); 124 | else 125 | Value = increment.AsInt + AsInt; 126 | } 127 | else 128 | { 129 | var l = new ArrayList(); 130 | if (IsList) 131 | { 132 | l.AddRange(AsList); 133 | } 134 | else 135 | { 136 | l.Add(Value); 137 | } 138 | if (increment.IsList) 139 | { 140 | l.AddRange(increment.AsList); 141 | } 142 | else 143 | { 144 | l.Add(increment); 145 | } 146 | Value = l; 147 | } 148 | } 149 | 150 | public ArrayList AsList 151 | { 152 | get { return IsList ? (Value as ArrayList) : (new ArrayList(new[] {Value})); } 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/OneOrMoreMatchTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class OneOrMoreMatchTests 7 | { 8 | [Test] 9 | public void Should_match_arg() 10 | { 11 | Assert.AreEqual( 12 | new MatchResult(true, 13 | new Pattern[0], 14 | new Pattern[] {new Argument("N", new ValueObject(9))}), 15 | new OneOrMore(new Argument("N")).Match(new Pattern[] {new Argument(null, new ValueObject(9))}) 16 | ); 17 | } 18 | 19 | [Test] 20 | public void Should_not_match_empty() 21 | { 22 | Assert.AreEqual( 23 | new MatchResult(false, 24 | new Pattern[0], 25 | new Pattern[0]), 26 | new OneOrMore(new Argument("N")).Match(new Pattern[0]) 27 | ); 28 | } 29 | 30 | [Test] 31 | public void Should_not_match_option() 32 | { 33 | Assert.AreEqual( 34 | new MatchResult(false, 35 | new Pattern[] {new Option("-x")}, 36 | new Pattern[0] 37 | ), 38 | new OneOrMore(new Argument("N")).Match(new Pattern[] {new Option("-x")}) 39 | ); 40 | } 41 | 42 | [Test] 43 | public void Should_match_all_args() 44 | { 45 | Assert.AreEqual( 46 | new MatchResult(true, 47 | new Pattern[0], 48 | new Pattern[] 49 | {new Argument("N", new ValueObject(9)), new Argument("N", new ValueObject(8))}), 50 | new OneOrMore(new Argument("N")).Match(new Pattern[] 51 | {new Argument(null, new ValueObject(9)), new Argument(null, new ValueObject(8))}) 52 | ); 53 | } 54 | 55 | [Test] 56 | public void Should_not_match_unknown_opt() 57 | { 58 | Assert.AreEqual( 59 | new MatchResult(true, 60 | new Pattern[] {new Option("-x")}, 61 | new Pattern[] 62 | {new Argument("N", new ValueObject(9)), new Argument("N", new ValueObject(8))}), 63 | new OneOrMore(new Argument("N")).Match(new Pattern[] 64 | {new Argument(null, new ValueObject(9)), new Option("-x"), new Argument(null, new ValueObject(8))}) 65 | ); 66 | } 67 | 68 | [Test] 69 | public void Should_not_match_unknown_arg() 70 | { 71 | Assert.AreEqual( 72 | new MatchResult(true, 73 | new Pattern[] {new Argument(null, new ValueObject(8))}, 74 | new Pattern[] {new Option("-a"), new Option("-a")} 75 | ), 76 | new OneOrMore(new Option("-a")).Match(new Pattern[] 77 | {new Option("-a"), new Argument(null, new ValueObject(8)), new Option("-a")}) 78 | ); 79 | } 80 | 81 | [Test] 82 | public void Should_not_match_unknown_opt_and_arg() 83 | { 84 | Assert.AreEqual( 85 | new MatchResult(false, 86 | new Pattern[] {new Argument(null, new ValueObject(98)), new Option("-x")}, 87 | new Pattern[0]), 88 | new OneOrMore(new Option("-a")).Match(new Pattern[] 89 | {new Argument(null, new ValueObject(98)), new Option("-x")}) 90 | ); 91 | } 92 | 93 | [Test] 94 | public void Should_match_all_opt_and_args() 95 | { 96 | Assert.AreEqual( 97 | new MatchResult(true, 98 | new Pattern[] {new Option("-x")}, 99 | new Pattern[] 100 | { 101 | new Option("-a"), new Argument("N", new ValueObject(1)), 102 | new Option("-a"), new Argument("N", new ValueObject(2)) 103 | } 104 | ), 105 | new OneOrMore(new Required(new Option("-a"), new Argument("N"))).Match(new Pattern[] 106 | { 107 | new Option("-a"), new Argument(null, new ValueObject(1)), 108 | new Option("-x"), 109 | new Option("-a"), new Argument(null, new ValueObject(2)) 110 | }) 111 | ); 112 | } 113 | 114 | [Test] 115 | public void Should_match_optional_arg() 116 | { 117 | Assert.AreEqual( 118 | new MatchResult(true, 119 | new Pattern[0], 120 | new Pattern[] {new Argument("N", new ValueObject(9))} 121 | ), 122 | new OneOrMore(new Optional(new Argument("N"))).Match(new Pattern[] 123 | {new Argument(null, new ValueObject(9))}) 124 | ); 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /src/DocoptNet.Tests/ParsePatternTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | 3 | namespace DocoptNet.Tests 4 | { 5 | [TestFixture] 6 | public class ParsePatternTests 7 | { 8 | private readonly Option[] _options = new[] {new Option("-h"), new Option("-v", "--verbose"), new Option("-f", "--file", 1)}; 9 | 10 | [Test] 11 | public void Test_parse_pattern_one_optional_option() 12 | { 13 | Assert.AreEqual(new Required(new Optional(new Option("-h"))), Docopt.ParsePattern("[ -h ]", _options)); 14 | } 15 | 16 | [Test] 17 | public void Test_parse_pattern_optional_oneormore_arg() 18 | { 19 | Assert.AreEqual(new Required(new Optional(new OneOrMore(new Argument("ARG")))), 20 | Docopt.ParsePattern("[ ARG ... ]", _options)); 21 | } 22 | 23 | [Test] 24 | public void Test_parse_pattern_either_options() 25 | { 26 | Assert.AreEqual(new Required(new Optional(new Either(new Option("-h"), new Option("-v", "--verbose")))), 27 | Docopt.ParsePattern("[ -h | -v ]", _options)); 28 | } 29 | 30 | [Test] 31 | public void Test_parse_pattern_either_options_and_optional_arg_opt() 32 | { 33 | Assert.AreEqual(new Required(new Required( 34 | new Either(new Option("-h"), 35 | new Required(new Option("-v", "--verbose"), 36 | new Optional(new Option("-f", "--file", 1)) 37 | ) 38 | ) 39 | ) 40 | ), 41 | Docopt.ParsePattern("( -h | -v [ --file ] )", _options)); 42 | } 43 | 44 | [Test] 45 | public void Test_parse_pattern_optional_and_oneormore_arg() 46 | { 47 | Assert.AreEqual(new Required(new Required( 48 | new Either(new Option("-h"), 49 | new Required(new Option("-v", "--verbose"), 50 | new Optional(new Option("-f", "--file", 1)), 51 | new OneOrMore(new Argument("N")) 52 | ) 53 | ) 54 | ) 55 | ), 56 | Docopt.ParsePattern("(-h|-v[--file=]N...)", _options)); 57 | } 58 | 59 | [Test] 60 | public void Test_parse_pattern_mix_of_either() 61 | { 62 | Assert.AreEqual(new Required(new Required(new Either( 63 | new Required(new Argument("N"), 64 | new Optional(new Either(new Argument("M"), 65 | new Required( 66 | new Either( 67 | new Argument("K"), 68 | new Argument("L"))) 69 | ) 70 | ) 71 | ) 72 | , new Required(new Argument("O"), new Argument("P")))) 73 | ), 74 | Docopt.ParsePattern("(N [M | (K | L)] | O P)", _options)); 75 | } 76 | 77 | [Test] 78 | public void Test_parse_pattern_option_with_optional_arg() 79 | { 80 | Assert.AreEqual(new Required(new Optional(new Option("-h")), new Optional(new Argument("N"))), 81 | Docopt.ParsePattern("[ -h ] [N]", _options)); 82 | } 83 | 84 | [Test] 85 | public void Test_parse_pattern_options_shortcut() 86 | { 87 | Assert.AreEqual(new Required(new Optional(new OptionsShortcut())), 88 | Docopt.ParsePattern("[options]", _options)); 89 | } 90 | 91 | [Test] 92 | public void Test_parse_pattern_options_shortcut_with_arg() 93 | { 94 | Assert.AreEqual(new Required(new Optional(new OptionsShortcut()), new Argument("A")), 95 | Docopt.ParsePattern("[options] A", _options)); 96 | } 97 | 98 | [Test] 99 | public void Test_parse_pattern_options_shortcut_with_long() 100 | { 101 | Assert.AreEqual(new Required(new Option("-v", "--verbose"), new Optional(new OptionsShortcut())), 102 | Docopt.ParsePattern("-v [options]", _options)); 103 | } 104 | 105 | [Test] 106 | public void Test_parse_pattern_arg_upper_case() 107 | { 108 | Assert.AreEqual(new Required(new Argument("ADD")), 109 | Docopt.ParsePattern("ADD", _options)); 110 | } 111 | 112 | [Test] 113 | public void Test_parse_pattern_arg_between_pointy_brackets() 114 | { 115 | Assert.AreEqual(new Required(new Argument("")), 116 | Docopt.ParsePattern("", _options)); 117 | } 118 | 119 | [Test] 120 | public void Test_parse_pattern_command() 121 | { 122 | Assert.AreEqual(new Required(new Command("add")), 123 | Docopt.ParsePattern("add", _options)); 124 | } 125 | 126 | } 127 | } -------------------------------------------------------------------------------- /src/DocoptNet/Pattern.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | 7 | namespace DocoptNet 8 | { 9 | internal abstract class Pattern 10 | { 11 | public ValueObject Value { get; set; } 12 | 13 | public virtual string Name 14 | { 15 | get { return ToString(); } 16 | } 17 | 18 | public virtual string GenerateCode() 19 | { 20 | return "// No code for " + Name; 21 | } 22 | 23 | public virtual Node ToNode() 24 | { 25 | return null; 26 | } 27 | 28 | // override object.Equals 29 | public override bool Equals(object obj) 30 | { 31 | // 32 | // See the full list of guidelines at 33 | // http://go.microsoft.com/fwlink/?LinkID=85237 34 | // and also the guidance for operator== at 35 | // http://go.microsoft.com/fwlink/?LinkId=85238 36 | // 37 | 38 | if (obj == null || GetType() != obj.GetType()) 39 | { 40 | return false; 41 | } 42 | 43 | return ToString() == obj.ToString(); 44 | } 45 | 46 | // override object.GetHashCode 47 | public override int GetHashCode() 48 | { 49 | return ToString().GetHashCode(); 50 | } 51 | 52 | public virtual bool HasChildren 53 | { 54 | get { return false; } 55 | } 56 | 57 | public IList Children { get; set; } 58 | 59 | public Pattern Fix() 60 | { 61 | FixIdentities(); 62 | FixRepeatingArguments(); 63 | return this; 64 | } 65 | 66 | /// 67 | /// Make pattern-tree tips point to same object if they are equal. 68 | /// 69 | /// 70 | public void FixIdentities(ICollection uniq = null) 71 | { 72 | var listUniq = uniq ?? Flat().Distinct().ToList(); 73 | for (int i = 0; i < Children.Count; i++) 74 | { 75 | var child = Children[i]; 76 | if (!child.HasChildren) 77 | { 78 | Debug.Assert(listUniq.Contains(child)); 79 | Children[i] = listUniq.First(p => p.Equals(child)); 80 | } 81 | else 82 | { 83 | child.FixIdentities(listUniq); 84 | } 85 | } 86 | } 87 | 88 | 89 | public Pattern FixRepeatingArguments() 90 | { 91 | var transform = Transform(this); 92 | var either = transform.Children.Select(c => c.Children); 93 | foreach (var aCase in either) 94 | { 95 | var cx = aCase.ToList(); 96 | var l = aCase.Where(e => cx.Count(c2 => c2.Equals(e)) > 1).ToList(); 97 | 98 | foreach (var e in l) 99 | { 100 | if (e is Argument || (e is Option && (e as Option).ArgCount > 0)) 101 | { 102 | if (e.Value == null) 103 | { 104 | e.Value = new ValueObject(new ArrayList()); 105 | } 106 | else if (!e.Value.IsList) 107 | { 108 | e.Value = 109 | new ValueObject(e.Value.ToString() 110 | .Split(new char[0], StringSplitOptions.RemoveEmptyEntries)); 111 | } 112 | } 113 | if (e is Command || (e is Option && (e as Option).ArgCount == 0)) 114 | { 115 | e.Value = new ValueObject(0); 116 | } 117 | } 118 | } 119 | return this; 120 | } 121 | 122 | /// 123 | /// Expand pattern into an (almost) equivalent one, but with single Either. 124 | /// Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d) 125 | /// Quirks: [-a] => (-a), (-a...) => (-a -a) 126 | /// 127 | /// 128 | /// 129 | public static Either Transform(Pattern pattern) 130 | { 131 | var result = new List>(); 132 | var groups = new List> {new List {pattern}}; 133 | while (groups.Count > 0) 134 | { 135 | var children = groups[0]; 136 | groups.RemoveAt(0); 137 | var parents = new[] 138 | { 139 | typeof (Required), typeof (Optional), typeof (OptionsShortcut), typeof (Either), typeof (OneOrMore) 140 | }; 141 | if (parents.Any(t => children.Any(c => c.GetType() == t))) 142 | { 143 | var child = children.First(c => parents.Contains(c.GetType())); 144 | children.Remove(child); 145 | if (child is Either) 146 | { 147 | foreach (var c in (child as Either).Children) 148 | { 149 | var l = new List {c}; 150 | l.AddRange(children); 151 | groups.Add(l); 152 | } 153 | } 154 | else if (child is OneOrMore) 155 | { 156 | var l = new List(); 157 | l.AddRange((child as OneOrMore).Children); 158 | l.AddRange((child as OneOrMore).Children); // add twice 159 | l.AddRange(children); 160 | groups.Add(l); 161 | } 162 | else 163 | { 164 | var l = new List(); 165 | if (child.HasChildren) 166 | l.AddRange(child.Children); 167 | l.AddRange(children); 168 | groups.Add(l); 169 | } 170 | } 171 | else 172 | { 173 | result.Add(children); 174 | } 175 | } 176 | return new Either(result.Select(r => new Required(r.ToArray()) as Pattern).ToArray()); 177 | } 178 | 179 | public virtual MatchResult Match(IList left, IEnumerable collected = null) 180 | { 181 | return new MatchResult(); 182 | } 183 | 184 | public abstract ICollection Flat(params Type[] types); 185 | 186 | /// 187 | /// Flattens the current patterns to the leaves only 188 | /// 189 | /// 190 | public IEnumerable Flat() 191 | { 192 | return Flat(new Type[0]); 193 | } 194 | } 195 | } -------------------------------------------------------------------------------- /src/DocoptNet.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2026 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{78331D52-9B59-4779-933C-EFE1C19D0D2B}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\paket.dependencies = ..\paket.dependencies 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{AE631D61-6BE6-438F-A535-955BB9A0218F}" 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{DC286C58-6D9D-4B8B-9C0D-F6E8CDB0D28E}" 14 | ProjectSection(SolutionItems) = preProject 15 | ..\build\Bootstrap.proj = ..\build\Bootstrap.proj 16 | ..\build\Build.bat = ..\build\Build.bat 17 | ..\build\Main.proj = ..\build\Main.proj 18 | EndProjectSection 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{B5A8AA26-1434-40D0-87BB-C1B8ED01F8D1}" 21 | ProjectSection(SolutionItems) = preProject 22 | NuGet\docopt.net.nuspec = NuGet\docopt.net.nuspec 23 | NuGet\install.ps1 = NuGet\install.ps1 24 | NuGet\Main.usage.txt = NuGet\Main.usage.txt 25 | EndProjectSection 26 | EndProject 27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocoptNet", "DocoptNet\DocoptNet.csproj", "{D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}" 28 | EndProject 29 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocoptNet.Tests", "DocoptNet.Tests\DocoptNet.Tests.csproj", "{80A9BB30-873C-47E9-8314-5E53AC66E8B7}" 30 | EndProject 31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NavalFate", "Examples\NavalFate\NavalFate.csproj", "{2B454F8B-D702-4281-80DF-367142007B0C}" 32 | EndProject 33 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testee", "Testee\Testee.csproj", "{C3490CAE-5A69-4852-BD61-50179D587D0D}" 34 | EndProject 35 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "T4DocoptNetHostApp", "T4DocoptNetHostApp\T4DocoptNetHostApp.csproj", "{5A641DFB-CB1A-4044-AF52-554CC8904449}" 36 | EndProject 37 | Global 38 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 39 | Debug|Any CPU = Debug|Any CPU 40 | Debug|Mixed Platforms = Debug|Mixed Platforms 41 | Debug|x86 = Debug|x86 42 | Release|Any CPU = Release|Any CPU 43 | Release|Mixed Platforms = Release|Mixed Platforms 44 | Release|x86 = Release|x86 45 | EndGlobalSection 46 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 47 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 48 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Debug|Any CPU.Build.0 = Debug|Any CPU 49 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 50 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 51 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Debug|x86.ActiveCfg = Debug|Any CPU 52 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Debug|x86.Build.0 = Debug|Any CPU 53 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Release|Any CPU.ActiveCfg = Release|Any CPU 54 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Release|Any CPU.Build.0 = Release|Any CPU 55 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 56 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Release|Mixed Platforms.Build.0 = Release|Any CPU 57 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Release|x86.ActiveCfg = Release|Any CPU 58 | {D87F19B8-A15F-4BC2-8E1C-47CC6038A0DF}.Release|x86.Build.0 = Release|Any CPU 59 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 60 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Debug|Any CPU.Build.0 = Debug|Any CPU 61 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 62 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 63 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Debug|x86.ActiveCfg = Debug|Any CPU 64 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 67 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Release|Mixed Platforms.Build.0 = Release|Any CPU 68 | {80A9BB30-873C-47E9-8314-5E53AC66E8B7}.Release|x86.ActiveCfg = Release|Any CPU 69 | {2B454F8B-D702-4281-80DF-367142007B0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 70 | {2B454F8B-D702-4281-80DF-367142007B0C}.Debug|Any CPU.Build.0 = Debug|Any CPU 71 | {2B454F8B-D702-4281-80DF-367142007B0C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 72 | {2B454F8B-D702-4281-80DF-367142007B0C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 73 | {2B454F8B-D702-4281-80DF-367142007B0C}.Debug|x86.ActiveCfg = Debug|Any CPU 74 | {2B454F8B-D702-4281-80DF-367142007B0C}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {2B454F8B-D702-4281-80DF-367142007B0C}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {2B454F8B-D702-4281-80DF-367142007B0C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 77 | {2B454F8B-D702-4281-80DF-367142007B0C}.Release|Mixed Platforms.Build.0 = Release|Any CPU 78 | {2B454F8B-D702-4281-80DF-367142007B0C}.Release|x86.ActiveCfg = Release|Any CPU 79 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 80 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Debug|Any CPU.Build.0 = Debug|Any CPU 81 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 82 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 83 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Debug|x86.ActiveCfg = Debug|Any CPU 84 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Release|Any CPU.ActiveCfg = Release|Any CPU 85 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Release|Any CPU.Build.0 = Release|Any CPU 86 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 87 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Release|Mixed Platforms.Build.0 = Release|Any CPU 88 | {C3490CAE-5A69-4852-BD61-50179D587D0D}.Release|x86.ActiveCfg = Release|Any CPU 89 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 90 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Debug|Any CPU.Build.0 = Debug|Any CPU 91 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 92 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 93 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Debug|x86.ActiveCfg = Debug|Any CPU 94 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Release|Any CPU.ActiveCfg = Release|Any CPU 95 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Release|Any CPU.Build.0 = Release|Any CPU 96 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 97 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Release|Mixed Platforms.Build.0 = Release|Any CPU 98 | {5A641DFB-CB1A-4044-AF52-554CC8904449}.Release|x86.ActiveCfg = Release|Any CPU 99 | EndGlobalSection 100 | GlobalSection(SolutionProperties) = preSolution 101 | HideSolutionNode = FALSE 102 | EndGlobalSection 103 | GlobalSection(NestedProjects) = preSolution 104 | {2B454F8B-D702-4281-80DF-367142007B0C} = {AE631D61-6BE6-438F-A535-955BB9A0218F} 105 | EndGlobalSection 106 | GlobalSection(ExtensibilityGlobals) = postSolution 107 | SolutionGuid = {3DEED27A-6D3F-4F2E-A5D5-1F71648F2413} 108 | EndGlobalSection 109 | EndGlobal 110 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/DocoptTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using NUnit.Framework; 4 | 5 | namespace DocoptNet.Tests 6 | { 7 | [TestFixture] 8 | public class DocoptTests 9 | { 10 | [Test] 11 | public void Test_tokens_from_pattern() 12 | { 13 | var tokens = Tokens.FromPattern("[ -h ]"); 14 | Assert.AreEqual(3, tokens.Count()); 15 | Assert.AreEqual("[", tokens.Current()); 16 | Assert.AreEqual("[", tokens.Move()); 17 | Assert.AreEqual("-h", tokens.Move()); 18 | Assert.AreEqual("]", tokens.Move()); 19 | } 20 | 21 | [Test] 22 | public void Test_set() 23 | { 24 | Assert.AreEqual(new Argument("N"), new Argument("N")); 25 | var l = new List {new Argument("N"), new Argument("N")}; 26 | var s = new HashSet(l); 27 | Assert.AreEqual(new Pattern[] {new Argument("N"),}, s.ToList()); 28 | } 29 | 30 | [Test] 31 | public void Test_issue_40_help() 32 | { 33 | var message = ""; 34 | var d = new Docopt(); 35 | d.PrintExit += (s, e) => message = e.Message; 36 | d.Apply("usage: prog --help-commands | --help", "--help"); 37 | StringAssert.StartsWith("usage", message); 38 | } 39 | 40 | [Test] 41 | public void Test_issue_106_exit() 42 | { 43 | Assert.Throws( 44 | () => new Docopt().Apply("usage: prog --help-commands | --help", "--help", exit: false)); 45 | } 46 | 47 | [Test] 48 | public void Test_issue_40_same_prefix() 49 | { 50 | var expected = new Dictionary 51 | { 52 | {"--aabb", new ValueObject(false)}, 53 | {"--aa", new ValueObject(true)} 54 | }; 55 | var actual = new Docopt().Apply("usage: prog --aabb | --aa", "--aa"); 56 | Assert.AreEqual(expected, actual); 57 | } 58 | 59 | [Test] 60 | public void Match_arg_only() 61 | { 62 | var expected = new Dictionary 63 | { 64 | {"-v", new ValueObject(false)}, 65 | {"A", new ValueObject("arg")} 66 | }; 67 | var actual = new Docopt().Apply(@"Usage: prog [-v] A 68 | 69 | Options: -v Be verbose.", "arg"); 70 | Assert.AreEqual(expected, actual); 71 | } 72 | 73 | [Test] 74 | public void Match_opt_and_arg() 75 | { 76 | var expected = new Dictionary 77 | { 78 | {"-v", new ValueObject(true)}, 79 | {"A", new ValueObject("arg")} 80 | }; 81 | var actual = new Docopt().Apply(@"Usage: prog [-v] A 82 | 83 | Options: -v Be verbose.", "-v arg"); 84 | Assert.AreEqual(expected, actual); 85 | } 86 | 87 | private const string DOC = @"Usage: prog [-vqr] [FILE] 88 | prog INPUT OUTPUT 89 | prog --help 90 | 91 | Options: 92 | -v print status messages 93 | -q report only file names 94 | -r show all occurrences of the same error 95 | --help 96 | "; 97 | 98 | [Test] 99 | public void Match_one_opt_with_arg() 100 | { 101 | var expected = new Dictionary 102 | { 103 | {"-v", new ValueObject(true)}, 104 | {"-q", new ValueObject(false)}, 105 | {"-r", new ValueObject(false)}, 106 | {"--help", new ValueObject(false)}, 107 | {"FILE", new ValueObject("file.py")}, 108 | {"INPUT", null}, 109 | {"OUTPUT", null} 110 | }; 111 | var actual = new Docopt().Apply(DOC, "-v file.py"); 112 | Assert.AreEqual(expected, actual); 113 | } 114 | 115 | [Test] 116 | public void Match_one_opt_only() 117 | { 118 | var expected = new Dictionary 119 | { 120 | {"-v", new ValueObject(true)}, 121 | {"-q", new ValueObject(false)}, 122 | {"-r", new ValueObject(false)}, 123 | {"--help", new ValueObject(false)}, 124 | {"FILE", null}, 125 | {"INPUT", null}, 126 | {"OUTPUT", null} 127 | }; 128 | var actual = new Docopt().Apply(DOC, "-v"); 129 | Assert.AreEqual(expected, actual); 130 | } 131 | 132 | [Test] 133 | public void No_match() 134 | { 135 | Assert.Throws(() => new Docopt().Apply(DOC, "-v input.py output.py")); 136 | } 137 | 138 | [Test] 139 | public void Non_existent_long() 140 | { 141 | Assert.Throws(() => new Docopt().Apply(DOC, "--fake")); 142 | } 143 | 144 | [Test] 145 | public void Should_exit_error_code_1() 146 | { 147 | var message = ""; 148 | var errorCode = 0; 149 | var d = new Docopt(); 150 | d.PrintExit += (s, e) => 151 | { 152 | message = e.Message; 153 | errorCode = e.ErrorCode; 154 | }; 155 | d.Apply(DOC, "--fake", exit:true); 156 | StringAssert.StartsWith("Usage", message); 157 | Assert.AreEqual(1, errorCode, "Should exit with error code 1 when exit=true and invalid args provided"); 158 | } 159 | 160 | [Test] 161 | public void Display_help() 162 | { 163 | var message = ""; 164 | var errorCode = 0; 165 | var d = new Docopt(); 166 | d.PrintExit += (s, e) => 167 | { 168 | message = e.Message; 169 | errorCode = e.ErrorCode; 170 | }; 171 | d.Apply(DOC, "--help"); 172 | StringAssert.StartsWith("Usage", message); 173 | Assert.AreEqual(0, errorCode); 174 | } 175 | 176 | [Test] 177 | public void Test_issue_59_assign_empty_string_to_long() 178 | { 179 | var expected = new Dictionary 180 | { 181 | {"--long", new ValueObject("")} 182 | }; 183 | var actual = new Docopt().Apply("usage: prog --long=", "--long="); 184 | Assert.AreEqual(expected, actual); 185 | } 186 | 187 | [Test] 188 | public void Test_issue_59_assign_empty_string_to_short() 189 | { 190 | var expected = new Dictionary 191 | { 192 | {"-l", new ValueObject("")} 193 | }; 194 | var actual = new Docopt().Apply("usage: prog -l \noptions: -l ", new[] {"-l", ""}); 195 | Assert.AreEqual(expected, actual); 196 | } 197 | 198 | [Test] 199 | public void Test_issue_68_options_shortcut_does_not_include_options_in_usage_pattern() 200 | { 201 | var args = new Docopt().Apply("usage: prog [-ab] [options]\noptions: -x\n -y", "-ax"); 202 | Assert.True(args["-a"].IsTrue); 203 | Assert.True(args["-b"].IsFalse); 204 | Assert.True(args["-x"].IsTrue); 205 | Assert.True(args["-y"].IsFalse); 206 | } 207 | } 208 | } -------------------------------------------------------------------------------- /src/Testee/Testee.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {C3490CAE-5A69-4852-BD61-50179D587D0D} 8 | Exe 9 | Properties 10 | Testee 11 | Testee 12 | v4.5 13 | 512 14 | ..\ 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | $(BuildOutDir)\$(MSBuildProjectName) 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | {d87f19b8-a15f-4bc2-8e1c-47cc6038a0df} 58 | DocoptNet 59 | 60 | 61 | 62 | 69 | 70 | 71 | 72 | 73 | 74 | ..\..\packages\Newtonsoft.Json\lib\netcore45\Newtonsoft.Json.dll 75 | True 76 | True 77 | 78 | 79 | 80 | 81 | 82 | 83 | ..\..\packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll 84 | True 85 | True 86 | 87 | 88 | 89 | 90 | 91 | 92 | ..\..\packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll 93 | True 94 | True 95 | 96 | 97 | 98 | 99 | 100 | 101 | ..\..\packages\Newtonsoft.Json\lib\net40\Newtonsoft.Json.dll 102 | True 103 | True 104 | 105 | 106 | 107 | 108 | 109 | 110 | ..\..\packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll 111 | True 112 | True 113 | 114 | 115 | 116 | 117 | 118 | 119 | ..\..\packages\Newtonsoft.Json\lib\portable-net40+sl4+wp7+win8\Newtonsoft.Json.dll 120 | True 121 | True 122 | 123 | 124 | 125 | 126 | 127 | 128 | ..\..\packages\Newtonsoft.Json\lib\portable-net45+wp80+win8\Newtonsoft.Json.dll 129 | True 130 | True 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/DocoptNet.Tests/ValueObjectTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using NUnit.Framework; 7 | 8 | namespace DocoptNet.Tests 9 | { 10 | [TestFixture] 11 | public class ValueObjectTests 12 | { 13 | [Test] 14 | public void IsNullOrEmpty_null_object_evaluates_true() 15 | { 16 | var systemUnderTest = new ValueObject(null); 17 | Assert.IsTrue(systemUnderTest.IsNullOrEmpty, "IsNullOrEmpty should evaluate to true when ValueObject wraps null."); 18 | } 19 | 20 | [Test] 21 | public void IsNullOrEmpty_empty_string_evaluates_true() 22 | { 23 | var systemUnderTest = new ValueObject(string.Empty); 24 | Assert.IsTrue(systemUnderTest.IsNullOrEmpty, "IsNullOrEmpty should evaluate to true when ValueObject wraps the empty string."); 25 | } 26 | 27 | [Test] 28 | public void IsNullOrEmpty_nonempty_string_evaluates_false() 29 | { 30 | var systemUnderTest = new ValueObject("a"); 31 | Assert.IsFalse(systemUnderTest.IsNullOrEmpty, "IsNullOrEmpty should evaluate to false when ValueObject wraps a non-empty string."); 32 | } 33 | 34 | [Test] 35 | public void IsNullOrEmpty_number_evaluates_false() 36 | { 37 | var systemUnderTest = new ValueObject(1); 38 | Assert.IsFalse(systemUnderTest.IsNullOrEmpty, "IsNullOrEmpty should evaluate to false when ValueObject wraps an integer."); 39 | } 40 | 41 | [Test] 42 | public void IsNullOrEmpty_bool_evaluates_false() 43 | { 44 | var systemUnderTest = new ValueObject(true); 45 | Assert.IsFalse(systemUnderTest.IsNullOrEmpty, "IsNullOrEmpty should evaluate to false when ValueObject wraps a bool."); 46 | systemUnderTest = new ValueObject(false); 47 | Assert.IsFalse(systemUnderTest.IsNullOrEmpty, "IsNullOrEmpty should evaluate to false when ValueObject wraps a bool."); 48 | } 49 | 50 | [Test] 51 | public void IsNullOrEmpty_list_evaluates_false() 52 | { 53 | var systemUnderTest = new ValueObject(new ArrayList()); 54 | Assert.IsFalse(systemUnderTest.IsNullOrEmpty, "IsNullOrEmpty should evaluate to false when ValueObject wraps a list."); 55 | } 56 | 57 | [Test] 58 | public void IsFalse_false_evaluates_true() 59 | { 60 | var systemUnderTest = new ValueObject(false); 61 | Assert.IsTrue(systemUnderTest.IsFalse, "IsFalse should evaluate to true when ValueObject wraps false."); 62 | } 63 | 64 | [Test] 65 | public void IsFalse_true_evaluates_false() 66 | { 67 | var systemUnderTest = new ValueObject(new ArrayList()); 68 | Assert.IsFalse(systemUnderTest.IsFalse, "IsFalse should evaluate to false when ValueObject wraps true."); 69 | } 70 | 71 | [Test] 72 | public void IsFalse_null_evaluates_false() 73 | { 74 | var systemUnderTest = new ValueObject(null); 75 | Assert.IsFalse(systemUnderTest.IsFalse, "IsFalse should evaluate to false when ValueObject wraps null."); 76 | } 77 | 78 | [Test] 79 | public void IsFalse_string_evaluates_false() 80 | { 81 | var systemUnderTest = new ValueObject("false"); 82 | Assert.IsFalse(systemUnderTest.IsFalse, "IsFalse should evaluate to false when ValueObject wraps a string."); 83 | } 84 | 85 | [Test] 86 | public void IsFalse_number_evaluates_false() 87 | { 88 | var systemUnderTest = new ValueObject(0); 89 | Assert.IsFalse(systemUnderTest.IsFalse, "IsFalse should evaluate to false when ValueObject wraps an integer."); 90 | } 91 | 92 | [Test] 93 | public void IsTrue_true_evaluates_true() 94 | { 95 | var systemUnderTest = new ValueObject(true); 96 | Assert.IsTrue(systemUnderTest.IsTrue, "IsTrue should evaluate to true when ValueObject wraps true."); 97 | } 98 | 99 | [Test] 100 | public void IsTrue_false_evaluates_false() 101 | { 102 | var systemUnderTest = new ValueObject(new ArrayList()); 103 | Assert.IsFalse(systemUnderTest.IsTrue, "IsTrue should evaluate to false when ValueObject wraps false."); 104 | } 105 | 106 | [Test] 107 | public void IsTrue_null_evaluates_false() 108 | { 109 | var systemUnderTest = new ValueObject(null); 110 | Assert.IsFalse(systemUnderTest.IsTrue, "IsTrue should evaluate to false when ValueObject wraps null."); 111 | } 112 | 113 | [Test] 114 | public void IsTrue_string_evaluates_false() 115 | { 116 | var systemUnderTest = new ValueObject("true"); 117 | Assert.IsFalse(systemUnderTest.IsTrue, "IsTrue should evaluate to false when ValueObject wraps a string."); 118 | } 119 | 120 | [Test] 121 | public void IsTrue_number_evaluates_false() 122 | { 123 | var systemUnderTest = new ValueObject(1); 124 | Assert.IsFalse(systemUnderTest.IsTrue, "IsTrue should evaluate to false when ValueObject wraps an integer."); 125 | } 126 | 127 | [Test] 128 | public void IsList_arraylist_evaluates_true() 129 | { 130 | var systemUnderTest = new ValueObject(new ArrayList()); 131 | Assert.IsTrue(systemUnderTest.IsList, "IsList should evaluate to true when ValueObject wraps an ArrayList."); 132 | } 133 | 134 | [Test] 135 | public void IsList_genericlist_evaluates_true() 136 | { 137 | var systemUnderTest = new ValueObject(new List()); 138 | Assert.IsTrue(systemUnderTest.IsList, "IsList should evaluate to true when ValueObject wraps an implementation of ICollection."); 139 | } 140 | 141 | [Test] 142 | public void IsList_string_evaluates_false() 143 | { 144 | var systemUnderTest = new ValueObject("1,2,3,4,5"); 145 | Assert.IsFalse(systemUnderTest.IsList, "IsList should evaluate to false when ValueObject wraps a string."); 146 | } 147 | 148 | [Test] 149 | public void IsOfTypeInt_int_evaluates_true() 150 | { 151 | var systemUnderTest = new ValueObject(1); 152 | Assert.IsTrue(systemUnderTest.IsOfTypeInt, "IsOfTypeInt should evaluate to true when ValueObject wraps an integer."); 153 | } 154 | 155 | [Test] 156 | public void IsOfTypeInt_integer_string_evaluates_false() 157 | { 158 | var systemUnderTest = new ValueObject("2"); 159 | Assert.IsFalse(systemUnderTest.IsOfTypeInt, "IsOfTypeInt should evaluate to false when ValueObject wraps a string that can be converted to an integer."); 160 | } 161 | 162 | [Test] 163 | public void IsOfTypeInt_noninteger_string_evaluates_false() 164 | { 165 | var systemUnderTest = new ValueObject("3.14"); 166 | Assert.IsFalse(systemUnderTest.IsOfTypeInt, "IsOfTypeInt should evaluate to false when ValueObject wraps a string that cannot be converted to an integer."); 167 | systemUnderTest = new ValueObject("abcde"); 168 | Assert.IsFalse(systemUnderTest.IsOfTypeInt, "IsOfTypeInt should evaluate to false when ValueObject wraps a string that cannot be converted to an integer."); 169 | } 170 | 171 | [Test] 172 | public void IsOfTypeInt_list_evaluates_false() 173 | { 174 | var systemUnderTest = new ValueObject(new ArrayList()); 175 | Assert.IsFalse(systemUnderTest.IsOfTypeInt, "IsOfTypeInt should evaluate to false when ValueObject wraps a list."); 176 | } 177 | 178 | [Test] 179 | public void IsOfTypeInt_null_evaluates_false() 180 | { 181 | var systemUnderTest = new ValueObject(null); 182 | Assert.IsFalse(systemUnderTest.IsOfTypeInt, "IsOfTypeInt should evaluate to false when ValueObject wraps null."); 183 | } 184 | 185 | [Test] 186 | public void IsInt_int_evaluates_true() 187 | { 188 | var systemUnderTest = new ValueObject(1); 189 | Assert.IsTrue(systemUnderTest.IsInt, "IsInt should evaluate to true when ValueObject wraps an integer."); 190 | } 191 | 192 | [Test] 193 | public void IsInt_integer_string_evaluates_true() 194 | { 195 | var systemUnderTest = new ValueObject("2"); 196 | Assert.IsTrue(systemUnderTest.IsInt, "IsInt should evaluate to true when ValueObject wraps a string that can be converted to an integer."); 197 | } 198 | 199 | [Test] 200 | public void IsInt_noninteger_string_evaluates_false() 201 | { 202 | var systemUnderTest = new ValueObject("3.14"); 203 | Assert.IsFalse(systemUnderTest.IsInt, "IsInt should evaluate to false when ValueObject wraps a string that cannot be converted to an integer."); 204 | systemUnderTest = new ValueObject("abcde"); 205 | Assert.IsFalse(systemUnderTest.IsInt, "IsInt should evaluate to false when ValueObject wraps a string that cannot be converted to an integer."); 206 | } 207 | 208 | [Test] 209 | public void IsInt_list_evaluates_false() 210 | { 211 | var systemUnderTest = new ValueObject(new ArrayList()); 212 | Assert.IsFalse(systemUnderTest.IsInt, "IsInt should evaluate to false when ValueObject wraps a list."); 213 | } 214 | 215 | [Test] 216 | public void IsInt_null_evaluates_false() 217 | { 218 | var systemUnderTest = new ValueObject(null); 219 | Assert.IsFalse(systemUnderTest.IsInt, "IsInt should evaluate to false when ValueObject wraps null."); 220 | } 221 | 222 | [Test] 223 | public void AsInt_int_evaluates_to_expected_value() 224 | { 225 | const int expected = 10; 226 | var systemUnderTest = new ValueObject(expected); 227 | Assert.AreEqual(expected, systemUnderTest.AsInt, "AsInt should return the value of the wrapped integer."); 228 | } 229 | 230 | [Test] 231 | public void AsInt_integer_string_evaluates_to_expected_value() 232 | { 233 | const int expected = 20; 234 | var systemUnderTest = new ValueObject(expected.ToString()); 235 | Assert.AreEqual(expected, systemUnderTest.AsInt, "AsInt should return the value of the wrapped string."); 236 | } 237 | 238 | [Test] 239 | public void IsString_empty_string_evalutes_true() 240 | { 241 | var systemUnderTest = new ValueObject(string.Empty); 242 | Assert.IsTrue(systemUnderTest.IsString, "IsString should evaluate to true when ValueObject wraps an empty string."); 243 | } 244 | 245 | [Test] 246 | public void IsString_nonempty_string_evalutes_true() 247 | { 248 | var systemUnderTest = new ValueObject("b"); 249 | Assert.IsTrue(systemUnderTest.IsString, "IsString should evaluate to true when ValueObject wraps a non-empty string."); 250 | } 251 | 252 | [Test] 253 | public void IsString_integer_string_evalutes_true() 254 | { 255 | var systemUnderTest = new ValueObject("5"); 256 | Assert.IsTrue(systemUnderTest.IsString, "IsString should evaluate to true when ValueObject wraps a string that can be converted to an integer."); 257 | } 258 | 259 | [Test] 260 | public void IsString_list_evalutes_false() 261 | { 262 | var systemUnderTest = new ValueObject(new ArrayList()); 263 | Assert.IsFalse(systemUnderTest.IsString, "IsString should evaluate to false when ValueObject wraps a list."); 264 | } 265 | 266 | [Test] 267 | public void IsString_null_evalutes_false() 268 | { 269 | var systemUnderTest = new ValueObject(null); 270 | Assert.IsFalse(systemUnderTest.IsString, "IsString should evaluate to false when ValueObject wraps null."); 271 | } 272 | 273 | [Test] 274 | public void AsList_list_evalutes_to_expected_collection() 275 | { 276 | var expected = new ArrayList() { 1, "xyz", null }; 277 | var systemUnderTest = new ValueObject(expected); 278 | CollectionAssert.AreEqual(expected, systemUnderTest.AsList, "AsList should evalute to a collection with the same elements in the same order as the wrapped list."); 279 | } 280 | 281 | [Test] 282 | public void AsList_array_evalutes_to_expected_collection() 283 | { 284 | var expected = new int[] { 1, 1, 2, 3, 5, 8 }; 285 | var systemUnderTest = new ValueObject(expected); 286 | CollectionAssert.AreEqual(expected, systemUnderTest.AsList, "AsList should evalute to a collection with the same elements in the same order as the wrapped array."); 287 | } 288 | 289 | [Test] 290 | public void AsList_string_evalutes_to_single_element_collection() 291 | { 292 | const string expected = "c"; 293 | var systemUnderTest = new ValueObject(expected); 294 | CollectionAssert.AreEqual(new [] { expected }, systemUnderTest.AsList, "AsList should evalute to a collection containing only the wrapped object."); 295 | } 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /src/LanguageAgnosticTests/testcases.docopt: -------------------------------------------------------------------------------- 1 | r"""Usage: prog 2 | 3 | """ 4 | $ prog 5 | {} 6 | 7 | $ prog --xxx 8 | "user-error" 9 | 10 | 11 | r"""Usage: prog [options] 12 | 13 | Options: -a All. 14 | 15 | """ 16 | $ prog 17 | {"-a": false} 18 | 19 | $ prog -a 20 | {"-a": true} 21 | 22 | $ prog -x 23 | "user-error" 24 | 25 | 26 | r"""Usage: prog [options] 27 | 28 | Options: --all All. 29 | 30 | """ 31 | $ prog 32 | {"--all": false} 33 | 34 | $ prog --all 35 | {"--all": true} 36 | 37 | $ prog --xxx 38 | "user-error" 39 | 40 | 41 | r"""Usage: prog [options] 42 | 43 | Options: -v, --verbose Verbose. 44 | 45 | """ 46 | $ prog --verbose 47 | {"--verbose": true} 48 | 49 | $ prog --ver 50 | {"--verbose": true} 51 | 52 | $ prog -v 53 | {"--verbose": true} 54 | 55 | 56 | r"""Usage: prog [options] 57 | 58 | Options: -p PATH 59 | 60 | """ 61 | $ prog -p home/ 62 | {"-p": "home/"} 63 | 64 | $ prog -phome/ 65 | {"-p": "home/"} 66 | 67 | $ prog -p 68 | "user-error" 69 | 70 | 71 | r"""Usage: prog [options] 72 | 73 | Options: --path 74 | 75 | """ 76 | $ prog --path home/ 77 | {"--path": "home/"} 78 | 79 | $ prog --path=home/ 80 | {"--path": "home/"} 81 | 82 | $ prog --pa home/ 83 | {"--path": "home/"} 84 | 85 | $ prog --pa=home/ 86 | {"--path": "home/"} 87 | 88 | $ prog --path 89 | "user-error" 90 | 91 | 92 | r"""Usage: prog [options] 93 | 94 | Options: -p PATH, --path= Path to files. 95 | 96 | """ 97 | $ prog -proot 98 | {"--path": "root"} 99 | 100 | 101 | r"""Usage: prog [options] 102 | 103 | Options: -p --path PATH Path to files. 104 | 105 | """ 106 | $ prog -p root 107 | {"--path": "root"} 108 | 109 | $ prog --path root 110 | {"--path": "root"} 111 | 112 | 113 | r"""Usage: prog [options] 114 | 115 | Options: 116 | -p PATH Path to files [default: ./] 117 | 118 | """ 119 | $ prog 120 | {"-p": "./"} 121 | 122 | $ prog -phome 123 | {"-p": "home"} 124 | 125 | 126 | r"""UsAgE: prog [options] 127 | 128 | OpTiOnS: --path= Path to files 129 | [dEfAuLt: /root] 130 | 131 | """ 132 | $ prog 133 | {"--path": "/root"} 134 | 135 | $ prog --path=home 136 | {"--path": "home"} 137 | 138 | 139 | r"""usage: prog [options] 140 | 141 | options: 142 | -a Add 143 | -r Remote 144 | -m Message 145 | 146 | """ 147 | $ prog -a -r -m Hello 148 | {"-a": true, 149 | "-r": true, 150 | "-m": "Hello"} 151 | 152 | $ prog -armyourass 153 | {"-a": true, 154 | "-r": true, 155 | "-m": "yourass"} 156 | 157 | $ prog -a -r 158 | {"-a": true, 159 | "-r": true, 160 | "-m": null} 161 | 162 | 163 | r"""Usage: prog [options] 164 | 165 | Options: --version 166 | --verbose 167 | 168 | """ 169 | $ prog --version 170 | {"--version": true, 171 | "--verbose": false} 172 | 173 | $ prog --verbose 174 | {"--version": false, 175 | "--verbose": true} 176 | 177 | $ prog --ver 178 | "user-error" 179 | 180 | $ prog --verb 181 | {"--version": false, 182 | "--verbose": true} 183 | 184 | 185 | r"""usage: prog [-a -r -m ] 186 | 187 | options: 188 | -a Add 189 | -r Remote 190 | -m Message 191 | 192 | """ 193 | $ prog -armyourass 194 | {"-a": true, 195 | "-r": true, 196 | "-m": "yourass"} 197 | 198 | 199 | r"""usage: prog [-armmsg] 200 | 201 | options: -a Add 202 | -r Remote 203 | -m Message 204 | 205 | """ 206 | $ prog -a -r -m Hello 207 | {"-a": true, 208 | "-r": true, 209 | "-m": "Hello"} 210 | 211 | 212 | r"""usage: prog -a -b 213 | 214 | options: 215 | -a 216 | -b 217 | 218 | """ 219 | $ prog -a -b 220 | {"-a": true, "-b": true} 221 | 222 | $ prog -b -a 223 | {"-a": true, "-b": true} 224 | 225 | $ prog -a 226 | "user-error" 227 | 228 | $ prog 229 | "user-error" 230 | 231 | 232 | r"""usage: prog (-a -b) 233 | 234 | options: -a 235 | -b 236 | 237 | """ 238 | $ prog -a -b 239 | {"-a": true, "-b": true} 240 | 241 | $ prog -b -a 242 | {"-a": true, "-b": true} 243 | 244 | $ prog -a 245 | "user-error" 246 | 247 | $ prog 248 | "user-error" 249 | 250 | 251 | r"""usage: prog [-a] -b 252 | 253 | options: -a 254 | -b 255 | 256 | """ 257 | $ prog -a -b 258 | {"-a": true, "-b": true} 259 | 260 | $ prog -b -a 261 | {"-a": true, "-b": true} 262 | 263 | $ prog -a 264 | "user-error" 265 | 266 | $ prog -b 267 | {"-a": false, "-b": true} 268 | 269 | $ prog 270 | "user-error" 271 | 272 | 273 | r"""usage: prog [(-a -b)] 274 | 275 | options: -a 276 | -b 277 | 278 | """ 279 | $ prog -a -b 280 | {"-a": true, "-b": true} 281 | 282 | $ prog -b -a 283 | {"-a": true, "-b": true} 284 | 285 | $ prog -a 286 | "user-error" 287 | 288 | $ prog -b 289 | "user-error" 290 | 291 | $ prog 292 | {"-a": false, "-b": false} 293 | 294 | 295 | r"""usage: prog (-a|-b) 296 | 297 | options: -a 298 | -b 299 | 300 | """ 301 | $ prog -a -b 302 | "user-error" 303 | 304 | $ prog 305 | "user-error" 306 | 307 | $ prog -a 308 | {"-a": true, "-b": false} 309 | 310 | $ prog -b 311 | {"-a": false, "-b": true} 312 | 313 | 314 | r"""usage: prog [ -a | -b ] 315 | 316 | options: -a 317 | -b 318 | 319 | """ 320 | $ prog -a -b 321 | "user-error" 322 | 323 | $ prog 324 | {"-a": false, "-b": false} 325 | 326 | $ prog -a 327 | {"-a": true, "-b": false} 328 | 329 | $ prog -b 330 | {"-a": false, "-b": true} 331 | 332 | 333 | r"""usage: prog """ 334 | $ prog 10 335 | {"": "10"} 336 | 337 | $ prog 10 20 338 | "user-error" 339 | 340 | $ prog 341 | "user-error" 342 | 343 | 344 | r"""usage: prog []""" 345 | $ prog 10 346 | {"": "10"} 347 | 348 | $ prog 10 20 349 | "user-error" 350 | 351 | $ prog 352 | {"": null} 353 | 354 | 355 | r"""usage: prog """ 356 | $ prog 10 20 40 357 | {"": "10", "": "20", "": "40"} 358 | 359 | $ prog 10 20 360 | "user-error" 361 | 362 | $ prog 363 | "user-error" 364 | 365 | 366 | r"""usage: prog [ ]""" 367 | $ prog 10 20 40 368 | {"": "10", "": "20", "": "40"} 369 | 370 | $ prog 10 20 371 | {"": "10", "": "20", "": null} 372 | 373 | $ prog 374 | "user-error" 375 | 376 | 377 | r"""usage: prog [ | ]""" 378 | $ prog 10 20 40 379 | "user-error" 380 | 381 | $ prog 20 40 382 | {"": null, "": "20", "": "40"} 383 | 384 | $ prog 385 | {"": null, "": null, "": null} 386 | 387 | 388 | r"""usage: prog ( --all | ) 389 | 390 | options: 391 | --all 392 | 393 | """ 394 | $ prog 10 --all 395 | {"": "10", "--all": true, "": null} 396 | 397 | $ prog 10 398 | {"": null, "--all": false, "": "10"} 399 | 400 | $ prog 401 | "user-error" 402 | 403 | 404 | r"""usage: prog [ ]""" 405 | $ prog 10 20 406 | {"": ["10", "20"]} 407 | 408 | $ prog 10 409 | {"": ["10"]} 410 | 411 | $ prog 412 | {"": []} 413 | 414 | 415 | r"""usage: prog [( )]""" 416 | $ prog 10 20 417 | {"": ["10", "20"]} 418 | 419 | $ prog 10 420 | "user-error" 421 | 422 | $ prog 423 | {"": []} 424 | 425 | 426 | r"""usage: prog NAME...""" 427 | $ prog 10 20 428 | {"NAME": ["10", "20"]} 429 | 430 | $ prog 10 431 | {"NAME": ["10"]} 432 | 433 | $ prog 434 | "user-error" 435 | 436 | 437 | r"""usage: prog [NAME]...""" 438 | $ prog 10 20 439 | {"NAME": ["10", "20"]} 440 | 441 | $ prog 10 442 | {"NAME": ["10"]} 443 | 444 | $ prog 445 | {"NAME": []} 446 | 447 | 448 | r"""usage: prog [NAME...]""" 449 | $ prog 10 20 450 | {"NAME": ["10", "20"]} 451 | 452 | $ prog 10 453 | {"NAME": ["10"]} 454 | 455 | $ prog 456 | {"NAME": []} 457 | 458 | 459 | r"""usage: prog [NAME [NAME ...]]""" 460 | $ prog 10 20 461 | {"NAME": ["10", "20"]} 462 | 463 | $ prog 10 464 | {"NAME": ["10"]} 465 | 466 | $ prog 467 | {"NAME": []} 468 | 469 | 470 | r"""usage: prog (NAME | --foo NAME) 471 | 472 | options: --foo 473 | 474 | """ 475 | $ prog 10 476 | {"NAME": "10", "--foo": false} 477 | 478 | $ prog --foo 10 479 | {"NAME": "10", "--foo": true} 480 | 481 | $ prog --foo=10 482 | "user-error" 483 | 484 | 485 | r"""usage: prog (NAME | --foo) [--bar | NAME] 486 | 487 | options: --foo 488 | options: --bar 489 | 490 | """ 491 | $ prog 10 492 | {"NAME": ["10"], "--foo": false, "--bar": false} 493 | 494 | $ prog 10 20 495 | {"NAME": ["10", "20"], "--foo": false, "--bar": false} 496 | 497 | $ prog --foo --bar 498 | {"NAME": [], "--foo": true, "--bar": true} 499 | 500 | 501 | r"""Naval Fate. 502 | 503 | Usage: 504 | prog ship new ... 505 | prog ship [] move [--speed=] 506 | prog ship shoot 507 | prog mine (set|remove) [--moored|--drifting] 508 | prog -h | --help 509 | prog --version 510 | 511 | Options: 512 | -h --help Show this screen. 513 | --version Show version. 514 | --speed= Speed in knots [default: 10]. 515 | --moored Mored (anchored) mine. 516 | --drifting Drifting mine. 517 | 518 | """ 519 | $ prog ship Guardian move 150 300 --speed=20 520 | {"--drifting": false, 521 | "--help": false, 522 | "--moored": false, 523 | "--speed": "20", 524 | "--version": false, 525 | "": ["Guardian"], 526 | "": "150", 527 | "": "300", 528 | "mine": false, 529 | "move": true, 530 | "new": false, 531 | "remove": false, 532 | "set": false, 533 | "ship": true, 534 | "shoot": false} 535 | 536 | 537 | r"""usage: prog --hello""" 538 | $ prog --hello 539 | {"--hello": true} 540 | 541 | 542 | r"""usage: prog [--hello=]""" 543 | $ prog 544 | {"--hello": null} 545 | 546 | $ prog --hello wrld 547 | {"--hello": "wrld"} 548 | 549 | 550 | r"""usage: prog [-o]""" 551 | $ prog 552 | {"-o": false} 553 | 554 | $ prog -o 555 | {"-o": true} 556 | 557 | 558 | r"""usage: prog [-opr]""" 559 | $ prog -op 560 | {"-o": true, "-p": true, "-r": false} 561 | 562 | 563 | r"""usage: prog --aabb | --aa""" 564 | $ prog --aa 565 | {"--aabb": false, "--aa": true} 566 | 567 | $ prog --a 568 | "user-error" # not a unique prefix 569 | 570 | # 571 | # Counting number of flags 572 | # 573 | 574 | r"""Usage: prog -v""" 575 | $ prog -v 576 | {"-v": true} 577 | 578 | 579 | r"""Usage: prog [-v -v]""" 580 | $ prog 581 | {"-v": 0} 582 | 583 | $ prog -v 584 | {"-v": 1} 585 | 586 | $ prog -vv 587 | {"-v": 2} 588 | 589 | 590 | r"""Usage: prog -v ...""" 591 | $ prog 592 | "user-error" 593 | 594 | $ prog -v 595 | {"-v": 1} 596 | 597 | $ prog -vv 598 | {"-v": 2} 599 | 600 | $ prog -vvvvvv 601 | {"-v": 6} 602 | 603 | 604 | r"""Usage: prog [-v | -vv | -vvv] 605 | 606 | This one is probably most readable user-friednly variant. 607 | 608 | """ 609 | $ prog 610 | {"-v": 0} 611 | 612 | $ prog -v 613 | {"-v": 1} 614 | 615 | $ prog -vv 616 | {"-v": 2} 617 | 618 | $ prog -vvvv 619 | "user-error" 620 | 621 | 622 | r"""usage: prog [--ver --ver]""" 623 | $ prog --ver --ver 624 | {"--ver": 2} 625 | 626 | 627 | # 628 | # Counting commands 629 | # 630 | 631 | r"""usage: prog [go]""" 632 | $ prog go 633 | {"go": true} 634 | 635 | 636 | r"""usage: prog [go go]""" 637 | $ prog 638 | {"go": 0} 639 | 640 | $ prog go 641 | {"go": 1} 642 | 643 | $ prog go go 644 | {"go": 2} 645 | 646 | $ prog go go go 647 | "user-error" 648 | 649 | r"""usage: prog go...""" 650 | $ prog go go go go go 651 | {"go": 5} 652 | 653 | # 654 | # [options] does not include options from usage-pattern 655 | # 656 | r"""usage: prog [options] [-a] 657 | 658 | options: -a 659 | -b 660 | """ 661 | $ prog -a 662 | {"-a": true, "-b": false} 663 | 664 | $ prog -aa 665 | "user-error" 666 | 667 | # 668 | # Test [options] shourtcut 669 | # 670 | 671 | r"""Usage: prog [options] A 672 | Options: 673 | -q Be quiet 674 | -v Be verbose. 675 | 676 | """ 677 | $ prog arg 678 | {"A": "arg", "-v": false, "-q": false} 679 | 680 | $ prog -v arg 681 | {"A": "arg", "-v": true, "-q": false} 682 | 683 | $ prog -q arg 684 | {"A": "arg", "-v": false, "-q": true} 685 | 686 | # 687 | # Test single dash 688 | # 689 | 690 | r"""usage: prog [-]""" 691 | 692 | $ prog - 693 | {"-": true} 694 | 695 | $ prog 696 | {"-": false} 697 | 698 | # 699 | # If argument is repeated, its value should always be a list 700 | # 701 | 702 | r"""usage: prog [NAME [NAME ...]]""" 703 | 704 | $ prog a b 705 | {"NAME": ["a", "b"]} 706 | 707 | $ prog 708 | {"NAME": []} 709 | 710 | # 711 | # Option's argument defaults to null/None 712 | # 713 | 714 | r"""usage: prog [options] 715 | options: 716 | -a Add 717 | -m Message 718 | 719 | """ 720 | $ prog -a 721 | {"-m": null, "-a": true} 722 | 723 | # 724 | # Test options without description 725 | # 726 | 727 | r"""usage: prog --hello""" 728 | $ prog --hello 729 | {"--hello": true} 730 | 731 | r"""usage: prog [--hello=]""" 732 | $ prog 733 | {"--hello": null} 734 | 735 | $ prog --hello wrld 736 | {"--hello": "wrld"} 737 | 738 | r"""usage: prog [-o]""" 739 | $ prog 740 | {"-o": false} 741 | 742 | $ prog -o 743 | {"-o": true} 744 | 745 | r"""usage: prog [-opr]""" 746 | $ prog -op 747 | {"-o": true, "-p": true, "-r": false} 748 | 749 | r"""usage: git [-v | --verbose]""" 750 | $ prog -v 751 | {"-v": true, "--verbose": false} 752 | 753 | r"""usage: git remote [-v | --verbose]""" 754 | $ prog remote -v 755 | {"remote": true, "-v": true, "--verbose": false} 756 | 757 | # 758 | # Test empty usage pattern 759 | # 760 | 761 | r"""usage: prog""" 762 | $ prog 763 | {} 764 | 765 | r"""usage: prog 766 | prog 767 | """ 768 | $ prog 1 2 769 | {"": "1", "": "2"} 770 | 771 | $ prog 772 | {"": null, "": null} 773 | 774 | r"""usage: prog 775 | prog 776 | """ 777 | $ prog 778 | {"": null, "": null} 779 | 780 | # 781 | # Option's argument should not capture default value from usage pattern 782 | # 783 | 784 | r"""usage: prog [--file=]""" 785 | $ prog 786 | {"--file": null} 787 | 788 | r"""usage: prog [--file=] 789 | 790 | options: --file 791 | 792 | """ 793 | $ prog 794 | {"--file": null} 795 | 796 | r"""Usage: prog [-a ] 797 | 798 | Options: -a, --address TCP address [default: localhost:6283]. 799 | 800 | """ 801 | $ prog 802 | {"--address": "localhost:6283"} 803 | 804 | # 805 | # If option with argument could be repeated, 806 | # its arguments should be accumulated into a list 807 | # 808 | 809 | r"""usage: prog --long= ...""" 810 | 811 | $ prog --long one 812 | {"--long": ["one"]} 813 | 814 | $ prog --long one --long two 815 | {"--long": ["one", "two"]} 816 | 817 | # 818 | # Test multiple elements repeated at once 819 | # 820 | 821 | r"""usage: prog (go --speed=)...""" 822 | $ prog go left --speed=5 go right --speed=9 823 | {"go": 2, "": ["left", "right"], "--speed": ["5", "9"]} 824 | 825 | # 826 | # Required options should work with option shortcut 827 | # 828 | 829 | r"""usage: prog [options] -a 830 | 831 | options: -a 832 | 833 | """ 834 | $ prog -a 835 | {"-a": true} 836 | 837 | # 838 | # If option could be repeated its defaults should be split into a list 839 | # 840 | 841 | r"""usage: prog [-o ]... 842 | 843 | options: -o [default: x] 844 | 845 | """ 846 | $ prog -o this -o that 847 | {"-o": ["this", "that"]} 848 | 849 | $ prog 850 | {"-o": ["x"]} 851 | 852 | r"""usage: prog [-o ]... 853 | 854 | options: -o [default: x y] 855 | 856 | """ 857 | $ prog -o this 858 | {"-o": ["this"]} 859 | 860 | $ prog 861 | {"-o": ["x", "y"]} 862 | 863 | # 864 | # Test stacked option's argument 865 | # 866 | 867 | r"""usage: prog -pPATH 868 | 869 | options: -p PATH 870 | 871 | """ 872 | $ prog -pHOME 873 | {"-p": "HOME"} 874 | 875 | # 876 | # Issue 56: Repeated mutually exclusive args give nested lists sometimes 877 | # 878 | 879 | r"""Usage: foo (--xx=x|--yy=y)...""" 880 | $ prog --xx=1 --yy=2 881 | {"--xx": ["1"], "--yy": ["2"]} 882 | 883 | # 884 | # POSIXly correct tokenization 885 | # 886 | 887 | r"""usage: prog []""" 888 | $ prog f.txt 889 | {"": "f.txt"} 890 | 891 | r"""usage: prog [--input=]...""" 892 | $ prog --input a.txt --input=b.txt 893 | {"--input": ["a.txt", "b.txt"]} 894 | 895 | # 896 | # Issue 85: `[options]` shourtcut with multiple subcommands 897 | # 898 | 899 | r"""usage: prog good [options] 900 | prog fail [options] 901 | 902 | options: --loglevel=N 903 | 904 | """ 905 | $ prog fail --loglevel 5 906 | {"--loglevel": "5", "fail": true, "good": false} 907 | 908 | # 909 | # Usage-section syntax 910 | # 911 | 912 | r"""usage:prog --foo""" 913 | $ prog --foo 914 | {"--foo": true} 915 | 916 | r"""PROGRAM USAGE: prog --foo""" 917 | $ prog --foo 918 | {"--foo": true} 919 | 920 | r"""Usage: prog --foo 921 | prog --bar 922 | NOT PART OF SECTION""" 923 | $ prog --foo 924 | {"--foo": true, "--bar": false} 925 | 926 | r"""Usage: 927 | prog --foo 928 | prog --bar 929 | 930 | NOT PART OF SECTION""" 931 | $ prog --foo 932 | {"--foo": true, "--bar": false} 933 | 934 | r"""Usage: 935 | prog --foo 936 | prog --bar 937 | NOT PART OF SECTION""" 938 | $ prog --foo 939 | {"--foo": true, "--bar": false} 940 | 941 | # 942 | # Options-section syntax 943 | # 944 | 945 | r"""Usage: prog [options] 946 | 947 | global options: --foo 948 | local options: --baz 949 | --bar 950 | other options: 951 | --egg 952 | --spam 953 | -not-an-option- 954 | 955 | """ 956 | $ prog --baz --egg 957 | {"--foo": false, "--baz": true, "--bar": false, "--egg": true, "--spam": false} 958 | --------------------------------------------------------------------------------