diff --git a/src/CommandLine/CommandLine.csproj b/src/CommandLine/CommandLine.csproj index 20050b11..b05cbb04 100644 --- a/src/CommandLine/CommandLine.csproj +++ b/src/CommandLine/CommandLine.csproj @@ -4,7 +4,7 @@ CommandLine Library netstandard2.0;net40;net45;net461 - $(DefineConstants);CSX_EITHER_INTERNAL;CSX_REM_EITHER_BEYOND_2;CSX_ENUM_INTERNAL;ERRH_INTERNAL;ERRH_DISABLE_INLINE_METHODS;CSX_MAYBE_INTERNAL;CSX_REM_EITHER_FUNC + $(DefineConstants);CSX_EITHER_INTERNAL;CSX_REM_EITHER_BEYOND_2;CSX_ENUM_INTERNAL;ERRH_INTERNAL;ERRH_DISABLE_INLINE_METHODS;CSX_MAYBE_INTERNAL;CSX_REM_EITHER_FUNC;CSX_REM_CRYPTORAND $(DefineConstants);SKIP_FSHARP true ..\..\CommandLine.snk @@ -47,7 +47,10 @@ - - + + + + + diff --git a/src/CommandLine/Core/InstanceBuilder.cs b/src/CommandLine/Core/InstanceBuilder.cs index 40b15917..4aff4080 100644 --- a/src/CommandLine/Core/InstanceBuilder.cs +++ b/src/CommandLine/Core/InstanceBuilder.cs @@ -28,14 +28,14 @@ public static ParserResult Build( var specProps = typeInfo.GetSpecifications(pi => SpecificationProperty.Create( Specification.FromProperty(pi), pi, Maybe.Nothing())) - .Memorize(); + .Memoize(); var specs = from pt in specProps select pt.Specification; var optionSpecs = specs .ThrowingValidate(SpecificationGuards.Lookup) .OfType() - .Memorize(); + .Memoize(); Func makeDefault = () => typeof(T).IsMutable() @@ -46,19 +46,19 @@ public static ParserResult Build( Func, ParserResult> notParsed = errs => new NotParsed(makeDefault().GetType().ToTypeInfo(), errs); - var argumentsList = arguments.Memorize(); + var argumentsList = arguments.Memoize(); Func> buildUp = () => { var tokenizerResult = tokenizer(argumentsList, optionSpecs); - var tokens = tokenizerResult.SucceededWith().Memorize(); + var tokens = tokenizerResult.SucceededWith().Memoize(); var partitions = TokenPartitioner.Partition( tokens, name => TypeLookup.FindTypeDescriptorAndSibling(name, optionSpecs, nameComparer)); - var optionsPartition = partitions.Item1.Memorize(); - var valuesPartition = partitions.Item2.Memorize(); - var errorsPartition = partitions.Item3.Memorize(); + var optionsPartition = partitions.Item1.Memoize(); + var valuesPartition = partitions.Item2.Memoize(); + var errorsPartition = partitions.Item3.Memoize(); var optionSpecPropsResult = OptionMapper.MapValues( @@ -80,7 +80,7 @@ public static ParserResult Build( .FromOptionSpecification()); var specPropsWithValue = - optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith()).Memorize(); + optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith()).Memoize(); var setPropertyErrors = new List(); @@ -104,7 +104,7 @@ public static ParserResult Build( .Concat(valueSpecPropsResult.SuccessfulMessages()) .Concat(validationErrors) .Concat(setPropertyErrors) - .Memorize(); + .Memoize(); var warnings = from e in allErrors where nonFatalErrors.Contains(e.Tag) select e; @@ -115,7 +115,7 @@ public static ParserResult Build( argumentsList.Any() ? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion)) : Enumerable.Empty() - ).Memorize(); + ).Memoize(); var result = argumentsList.Any() ? preprocessorErrors.Any() diff --git a/src/CommandLine/Core/OptionMapper.cs b/src/CommandLine/Core/OptionMapper.cs index 102646a9..18349b40 100644 --- a/src/CommandLine/Core/OptionMapper.cs +++ b/src/CommandLine/Core/OptionMapper.cs @@ -43,7 +43,7 @@ select Tuple.Create( ((OptionSpecification)pt.Specification).FromOptionSpecification())))) : Tuple.Create(pt, Maybe.Nothing()); } - ).Memorize(); + ).Memoize(); return Result.Succeed( sequencesAndErrors.Select(se => se.Item1), sequencesAndErrors.Select(se => se.Item2).OfType>().Select(se => se.Value)); diff --git a/src/CommandLine/Core/TokenPartitioner.cs b/src/CommandLine/Core/TokenPartitioner.cs index cc1d8c26..be38a6d0 100644 --- a/src/CommandLine/Core/TokenPartitioner.cs +++ b/src/CommandLine/Core/TokenPartitioner.cs @@ -17,16 +17,16 @@ Tuple>>, IEnumerable tokenComparer = ReferenceEqualityComparer.Default; - var tokenList = tokens.Memorize(); + var tokenList = tokens.Memoize(); var switches = new HashSet(Switch.Partition(tokenList, typeLookup), tokenComparer); var scalars = new HashSet(Scalar.Partition(tokenList, typeLookup), tokenComparer); var sequences = new HashSet(Sequence.Partition(tokenList, typeLookup), tokenComparer); var nonOptions = tokenList .Where(t => !switches.Contains(t)) .Where(t => !scalars.Contains(t)) - .Where(t => !sequences.Contains(t)).Memorize(); - var values = nonOptions.Where(v => v.IsValue()).Memorize(); - var errors = nonOptions.Except(values, (IEqualityComparer)ReferenceEqualityComparer.Default).Memorize(); + .Where(t => !sequences.Contains(t)).Memoize(); + var values = nonOptions.Where(v => v.IsValue()).Memoize(); + var errors = nonOptions.Except(values, (IEqualityComparer)ReferenceEqualityComparer.Default).Memoize(); return Tuple.Create( KeyValuePairHelper.ForSwitch(switches) diff --git a/src/CommandLine/Core/Tokenizer.cs b/src/CommandLine/Core/Tokenizer.cs index fb241579..9fd8863c 100644 --- a/src/CommandLine/Core/Tokenizer.cs +++ b/src/CommandLine/Core/Tokenizer.cs @@ -34,11 +34,11 @@ public static Result, Error> Tokenize( ? TokenizeLongName(arg, onError) : TokenizeShortName(arg, nameLookup) select token) - .Memorize(); + .Memoize(); - var normalized = normalize(tokens).Memorize(); + var normalized = normalize(tokens).Memoize(); - var unkTokens = (from t in normalized where t.IsName() && nameLookup(t.Text) == NameLookupResult.NoOptionFound select t).Memorize(); + var unkTokens = (from t in normalized where t.IsName() && nameLookup(t.Text) == NameLookupResult.NoOptionFound select t).Memoize(); return Result.Succeed(normalized.Where(x => !unkTokens.Contains(x)), errors.Concat(from t in unkTokens select new UnknownOptionError(t.Text))); } @@ -60,12 +60,12 @@ public static Result, Error> ExplodeOptionList( Result, Error> tokenizerResult, Func> optionSequenceWithSeparatorLookup) { - var tokens = tokenizerResult.SucceededWith().Memorize(); + var tokens = tokenizerResult.SucceededWith().Memoize(); var replaces = tokens.Select((t, i) => optionSequenceWithSeparatorLookup(t.Text) .MapValueOrDefault(sep => Tuple.Create(i + 1, sep), - Tuple.Create(-1, '\0'))).SkipWhile(x => x.Item1 < 0).Memorize(); + Tuple.Create(-1, '\0'))).SkipWhile(x => x.Item1 < 0).Memoize(); var exploded = tokens.Select((t, i) => replaces.FirstOrDefault(x => x.Item1 == i).ToMaybe() @@ -205,4 +205,4 @@ private static IEnumerable TokenizeLongName( } } } -} \ No newline at end of file +} diff --git a/src/CommandLine/Infrastructure/Either.cs b/src/CommandLine/Infrastructure/CSharpx/Either.cs similarity index 91% rename from src/CommandLine/Infrastructure/Either.cs rename to src/CommandLine/Infrastructure/CSharpx/Either.cs index 71666dc2..f3c80585 100644 --- a/src/CommandLine/Infrastructure/Either.cs +++ b/src/CommandLine/Infrastructure/CSharpx/Either.cs @@ -1,6 +1,5 @@ -//Use project level define(s) when referencing with Paket. -//#define CSX_EITHER_INTERNAL // Uncomment this to set visibility to internal. -//#define CSX_REM_MAYBE_FUNC // Uncomment this to remove dependency to Maybe.cs. +//#define CSX_EITHER_INTERNAL // Uncomment or define at build time to set accessibility to internal. +//#define CSX_REM_MAYBE_FUNC // Uncomment or define at build time to remove dependency to Maybe.cs. using System; @@ -133,8 +132,7 @@ public static Either Fail(string message) public static Either Bind(Either either, Func> func) { TRight right; - if (either.MatchRight(out right)) - { + if (either.MatchRight(out right)) { return func(right); } return Either.Left(either.GetLeft()); @@ -148,8 +146,7 @@ public static Either Bind(Either Map(Either either, Func func) { TRight right; - if (either.MatchRight(out right)) - { + if (either.MatchRight(out right)) { return Either.Right(func(right)); } return Either.Left(either.GetLeft()); @@ -164,8 +161,7 @@ public static Either Map(Either Bimap(Either either, Func mapLeft, Func mapRight) { TRight right; - if (either.MatchRight(out right)) - { + if (either.MatchRight(out right)) { return Either.Right(mapRight(right)); } return Either.Left(mapLeft(either.GetLeft())); @@ -196,9 +192,10 @@ public static Either SelectMany(this Eit public static TRight GetOrFail(Either either) { TRight value; - if (either.MatchRight(out value)) + if (either.MatchRight(out value)) { return value; - throw new ArgumentException("either", string.Format("The either value was Left {0}", either)); + } + throw new ArgumentException(nameof(either), string.Format("The either value was Left {0}", either)); } /// @@ -224,12 +221,10 @@ public static TRight GetRightOrDefault(Either eith /// public static Either Try(Func func) { - try - { + try { return new Right(func()); } - catch (Exception ex) - { + catch (Exception ex) { return new Left(ex); } } @@ -244,10 +239,9 @@ public static Either Cast(object obj) } #if !CSX_REM_MAYBE_FUNC - public static Either OfMaybe(Maybe maybe, TLeft left) + public static Either FromMaybe(Maybe maybe, TLeft left) { - if (maybe.Tag == MaybeType.Just) - { + if (maybe.Tag == MaybeType.Just) { return Either.Right(((Just)maybe).Value); } return Either.Left(left); @@ -269,8 +263,7 @@ static class EitherExtensions public static void Match(this Either either, Action ifLeft, Action ifRight) { TLeft left; - if (either.MatchLeft(out left)) - { + if (either.MatchLeft(out left)) { ifLeft(left); return; } @@ -279,7 +272,7 @@ public static void Match(this Either either, Actio #endregion /// - /// Equivalent to monadic operation. + /// Equivalent to monadic operation. /// Builds a value in case by default. /// public static Either ToEither(this TRight value) diff --git a/src/CommandLine/Infrastructure/CSharpx/EnumerableExtensions.cs b/src/CommandLine/Infrastructure/CSharpx/EnumerableExtensions.cs new file mode 100644 index 00000000..b668eb46 --- /dev/null +++ b/src/CommandLine/Infrastructure/CSharpx/EnumerableExtensions.cs @@ -0,0 +1,463 @@ +//#define CSX_ENUM_INTERNAL // Uncomment or define at build time to set accessibility to internal. +//#define CSX_REM_MAYBE_FUNC // Uncomment or define at build time to remove dependency to Maybe.cs. +//#define CSX_REM_CRYPTORAND // Uncomment or define at build time to remove dependency to CryptoRandom.cs. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Text; +using LinqEnumerable = System.Linq.Enumerable; + +namespace CSharpx +{ +#if !CSX_ENUM_INTERNAL + public +#endif + static class EnumerableExtensions + { +#if !CSX_REM_MAYBE_FUNC + /// + /// Safe function that returns Just(first element) or None. + /// + public static Maybe TryHead(this IEnumerable source) + { + using (var e = source.GetEnumerator()) { + return e.MoveNext() + ? Maybe.Just(e.Current) + : Maybe.Nothing(); + } + } + + /// + /// Turns an empty sequence to Nothing, otherwise Just(sequence). + /// + public static Maybe> ToMaybe(this IEnumerable source) + { + using (var e = source.GetEnumerator()) { + return e.MoveNext() + ? Maybe.Just(source) + : Maybe.Nothing>(); + } + } +#endif + + private static IEnumerable AssertCountImpl(IEnumerable source, + int count, Func errorSelector) + { + var collection = source as ICollection; // Optimization for collections + if (collection != null) + { + if (collection.Count != count) { + throw errorSelector(collection.Count.CompareTo(count), count); + } + return source; + } + + return ExpectingCountYieldingImpl(source, count, errorSelector); + } + + private static IEnumerable ExpectingCountYieldingImpl(IEnumerable source, + int count, Func errorSelector) + { + var iterations = 0; + foreach (var element in source) + { + iterations++; + if (iterations > count) { + throw errorSelector(1, count); + } + yield return element; + } + if (iterations != count) { + throw errorSelector(-1, count); + } + } + + /// + /// Returns the Cartesian product of two sequences by combining each element of the first set with each in the second + /// and applying the user=define projection to the pair. + /// + public static IEnumerable Cartesian(this IEnumerable first, IEnumerable second, Func resultSelector) + { + if (first == null) throw new ArgumentNullException(nameof(first)); + if (second == null) throw new ArgumentNullException(nameof(second)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return from element1 in first + from element2 in second // TODO buffer to avoid multiple enumerations + select resultSelector(element1, element2); + } + + /// + /// Prepends a single value to a sequence. + /// + public static IEnumerable Prepend(this IEnumerable source, TSource value) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + return LinqEnumerable.Concat(LinqEnumerable.Repeat(value, 1), source); + } + + /// + /// Returns a sequence consisting of the head element and the given tail elements. + /// + public static IEnumerable Concat(this T head, IEnumerable tail) + { + if (tail == null) throw new ArgumentNullException(nameof(tail)); + + return tail.Prepend(head); + } + + /// + /// Returns a sequence consisting of the head elements and the given tail element. + /// + public static IEnumerable Concat(this IEnumerable head, T tail) + { + if (head == null) throw new ArgumentNullException(nameof(head)); + + return LinqEnumerable.Concat(head, LinqEnumerable.Repeat(tail, 1)); + } + + /// + /// Excludes elements from a sequence starting at a given index + /// + /// The type of the elements of the sequence + public static IEnumerable Exclude(this IEnumerable sequence, int startIndex, int count) + { + if (sequence == null) throw new ArgumentNullException(nameof(sequence)); + if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex)); + if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); + + return ExcludeImpl(sequence, startIndex, count); + } + + private static IEnumerable ExcludeImpl(IEnumerable sequence, int startIndex, int count) + { + var index = -1; + var endIndex = startIndex + count; + using (var iter = sequence.GetEnumerator()) + { + // yield the first part of the sequence + while (iter.MoveNext() && ++index < startIndex) { + yield return iter.Current; + } + // skip the next part (up to count elements) + while (++index < endIndex && iter.MoveNext()) { + continue; + } + // yield the remainder of the sequence + while (iter.MoveNext()) { + yield return iter.Current; + } + } + } + + /// + /// Returns a sequence of + /// where the key is the zero-based index of the value in the source + /// sequence. + /// + public static IEnumerable> Index(this IEnumerable source) + { + return source.Index(0); + } + + /// + /// Returns a sequence of + /// where the key is the index of the value in the source sequence. + /// An additional parameter specifies the starting index. + /// + public static IEnumerable> Index(this IEnumerable source, int startIndex) + { + return source.Select((element, index) => new KeyValuePair(startIndex + index, element)); + } + + /// + /// Returns the result of applying a function to a sequence of + /// 1 element. + /// + public static TResult Fold(this IEnumerable source, Func folder) + { + return FoldImpl(source, 1, folder, null, null, null); + } + + /// + /// Returns the result of applying a function to a sequence of + /// 2 elements. + /// + public static TResult Fold(this IEnumerable source, Func folder) + { + return FoldImpl(source, 2, null, folder, null, null); + } + + /// + /// Returns the result of applying a function to a sequence of + /// 3 elements. + /// + public static TResult Fold(this IEnumerable source, Func folder) + { + return FoldImpl(source, 3, null, null, folder, null); + } + + /// + /// Returns the result of applying a function to a sequence of + /// 4 elements. + /// + public static TResult Fold(this IEnumerable source, Func folder) + { + return FoldImpl(source, 4, null, null, null, folder); + } + + static TResult FoldImpl(IEnumerable source, int count, + Func folder1, + Func folder2, + Func folder3, + Func folder4) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (count == 1 && folder1 == null + || count == 2 && folder2 == null + || count == 3 && folder3 == null + || count == 4 && folder4 == null) + { // ReSharper disable NotResolvedInText + throw new ArgumentNullException("folder"); // ReSharper restore NotResolvedInText + } + + var elements = new T[count]; + foreach (var e in AssertCountImpl( + source.Index(), count, OnFolderSourceSizeErrorSelector)) { + elements[e.Key] = e.Value; + } + + switch (count) { + case 1: return folder1(elements[0]); + case 2: return folder2(elements[0], elements[1]); + case 3: return folder3(elements[0], elements[1], elements[2]); + case 4: return folder4(elements[0], elements[1], elements[2], elements[3]); + default: throw new NotSupportedException(); + } + } + + static readonly Func OnFolderSourceSizeErrorSelector = OnFolderSourceSizeError; + + static Exception OnFolderSourceSizeError(int cmp, int count) + { + var message = cmp < 0 + ? "Sequence contains too few elements when exactly {0} {1} expected" + : "Sequence contains too many elements when exactly {0} {1} expected"; + return new Exception(string.Format(message, count.ToString("N0"), count == 1 ? "was" : "were")); + } + + /// + /// Immediately executes the given action on each element in the source sequence. + /// + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (action == null) throw new ArgumentNullException(nameof(action)); + + foreach (var element in source) { + action(element); + } + } + + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// predecessor, with the exception of the first element which is + /// only returned as the predecessor of the second element. + /// + public static IEnumerable Pairwise(this IEnumerable source, Func resultSelector) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + + return PairwiseImpl(source, resultSelector); + } + + private static IEnumerable PairwiseImpl(this IEnumerable source, Func resultSelector) + { + Debug.Assert(source != null); + Debug.Assert(resultSelector != null); + + using (var e = source.GetEnumerator()) { + if (!e.MoveNext()) { + yield break; + } + + var previous = e.Current; + while (e.MoveNext()) { + yield return resultSelector(previous, e.Current); + previous = e.Current; + } + } + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + + return ToDelimitedStringImpl(source, delimiter, (sb, e) => sb.Append(e)); + } + + static string ToDelimitedStringImpl(IEnumerable source, string delimiter, Func append) + { + Debug.Assert(source != null); + Debug.Assert(append != null); + + delimiter = delimiter ?? CultureInfo.CurrentCulture.TextInfo.ListSeparator; + var sb = new StringBuilder(); + var i = 0; + + foreach (var value in source) { + if (i++ > 0) sb.Append(delimiter); + append(sb, value); + } + + return sb.ToString(); + } + + /// + /// Return everything except first element and throws exception if empty. + /// + public static IEnumerable Tail(this IEnumerable source) + { + using (var e = source.GetEnumerator()) { + if (e.MoveNext()) { + while (e.MoveNext()) { + yield return e.Current; + } + } + else { + throw new ArgumentException("Source sequence cannot be empty", nameof(source)); + } + } + } + + /// + /// Return everything except first element without throwing exception if empty. + /// + public static IEnumerable TailNoFail(this IEnumerable source) + { + using (var e = source.GetEnumerator()) + { + if (e.MoveNext()) { + while (e.MoveNext()) { + yield return e.Current; + } + } + } + } + + /// + /// Captures current state of a sequence. + /// + public static IEnumerable Memoize(this IEnumerable source) + { + return source.GetType().IsArray ? source : source.ToArray(); + } + + /// + /// Creates an immutable copy of a sequence. + /// + public static IEnumerable Materialize(this IEnumerable source) + { + if (source is MaterializedEnumerable || source.GetType().IsArray) { + return source; + } + return new MaterializedEnumerable(source); + } + + private class MaterializedEnumerable : IEnumerable + { + private readonly ICollection inner; + + public MaterializedEnumerable(IEnumerable enumerable) + { + inner = enumerable as ICollection ?? enumerable.ToArray(); + } + + public IEnumerator GetEnumerator() + { + return inner.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + /// + /// Selects a random element. + /// + public static T Choice(this IEnumerable source) + { +#if CSX_REM_CRYPTORAND + var index = new Random().Next(source.Count() - 1); +#else + var index = new CryptoRandom().Next(source.Count() - 1); +#endif + return source.ElementAt(index); + } + + /// + /// Takes an element and a sequence and `intersperses' that element between its elements. + /// + public static IEnumerable Intersperse(this IEnumerable source, T element) + { + if (element == null) throw new ArgumentNullException(nameof(element)); + + var count = source.Count(); + var last = count - 1; + for (var i = 0; i < count; i++) { + yield return source.ElementAt(i); + if (i != last) { + yield return element; + } + } + } + + /// + /// Flattens a sequence by one level. + /// + public static IEnumerable FlattenOnce(this IEnumerable> source) + { + foreach (var element in source) { + foreach (var subelement in element) { + yield return subelement; + } + } + } + + /// + /// Reduces a sequence of strings to a sequence of parts, splitted by space, + /// of each original string. + /// + public static IEnumerable FlattenOnce(this IEnumerable source) + { + foreach (var element in source) { + var parts = element.Split(); + foreach (var part in parts) { + yield return part; + } + } + } + } +} \ No newline at end of file diff --git a/src/CommandLine/Infrastructure/Maybe.cs b/src/CommandLine/Infrastructure/CSharpx/Maybe.cs similarity index 92% rename from src/CommandLine/Infrastructure/Maybe.cs rename to src/CommandLine/Infrastructure/CSharpx/Maybe.cs index 63451865..1dacf4e8 100644 --- a/src/CommandLine/Infrastructure/Maybe.cs +++ b/src/CommandLine/Infrastructure/CSharpx/Maybe.cs @@ -1,6 +1,5 @@ -//Use project level define(s) when referencing with Paket. -//#define CSX_MAYBE_INTERNAL // Uncomment this to set visibility to internal. -//#define CSX_REM_EITHER_FUNC // Uncomment this to remove dependency to Either.cs. +//#define CSX_MAYBE_INTERNAL // Uncomment or define at build time set accessibility to internal. +//#define CSX_REM_EITHER_FUNC // Uncomment or define at build time to remove dependency to Either.cs. using System; using System.Collections.Generic; @@ -165,8 +164,7 @@ public static Maybe> Merge(Maybe first, Maybe seco { T1 value1; T2 value2; - if (first.MatchJust(out value1) && second.MatchJust(out value2)) - { + if (first.MatchJust(out value1) && second.MatchJust(out value2)) { return Maybe.Just(Tuple.Create(value1, value2)); } return Maybe.Nothing>(); @@ -176,10 +174,9 @@ public static Maybe> Merge(Maybe first, Maybe seco /// /// Maps Either Right value to Maybe Just, otherwise Maybe Nothing. /// - public static Maybe OfEither(Either either) + public static Maybe FromEither(Either either) { - if (either.Tag == EitherType.Right) - { + if (either.Tag == EitherType.Right) { return Maybe.Just(((Right)either).Value); } return Maybe.Nothing(); @@ -202,8 +199,7 @@ static class MaybeExtensions public static void Match(this Maybe maybe, Action ifJust, Action ifNothing) { T value; - if (maybe.MatchJust(out value)) - { + if (maybe.MatchJust(out value)) { ifJust(value); return; } @@ -217,8 +213,7 @@ public static void Match(this Maybe> maybe, Action { T1 value1; T2 value2; - if (maybe.MatchJust(out value1, out value2)) - { + if (maybe.MatchJust(out value1, out value2)) { ifJust(value1, value2); return; } @@ -231,8 +226,7 @@ public static void Match(this Maybe> maybe, Action public static bool MatchJust(this Maybe> maybe, out T1 value1, out T2 value2) { Tuple value; - if (maybe.MatchJust(out value)) - { + if (maybe.MatchJust(out value)) { value1 = value.Item1; value2 = value.Item2; return true; @@ -296,13 +290,12 @@ public static Maybe SelectMany( #region Do Semantic /// - /// If contans a value executes an delegate over it. + /// If contains a value executes an delegate over it. /// public static void Do(this Maybe maybe, Action action) { T value; - if (maybe.MatchJust(out value)) - { + if (maybe.MatchJust(out value)) { action(value); } } @@ -314,8 +307,7 @@ public static void Do(this Maybe> maybe, Action ac { T1 value1; T2 value2; - if (maybe.MatchJust(out value1, out value2)) - { + if (maybe.MatchJust(out value1, out value2)) { action(value1, value2); } } @@ -343,8 +335,7 @@ public static bool IsNothing(this Maybe maybe) public static T FromJust(this Maybe maybe) { T value; - if (maybe.MatchJust(out value)) - { + if (maybe.MatchJust(out value)) { return value; } return default(T); @@ -356,8 +347,7 @@ public static T FromJust(this Maybe maybe) public static T FromJustOrFail(this Maybe maybe, Exception exceptionToThrow = null) { T value; - if (maybe.MatchJust(out value)) - { + if (maybe.MatchJust(out value)) { return value; } throw exceptionToThrow ?? new ArgumentException("Value empty."); @@ -387,8 +377,7 @@ public static T2 MapValueOrDefault(this Maybe maybe, Func fu public static IEnumerable ToEnumerable(this Maybe maybe) { T value; - if (maybe.MatchJust(out value)) - { + if (maybe.MatchJust(out value)) { return Enumerable.Empty().Concat(new[] { value }); } return Enumerable.Empty(); diff --git a/src/CommandLine/Infrastructure/EnumerableExtensions.cs b/src/CommandLine/Infrastructure/EnumerableExtensions.cs index fa9a7951..bdada5de 100644 --- a/src/CommandLine/Infrastructure/EnumerableExtensions.cs +++ b/src/CommandLine/Infrastructure/EnumerableExtensions.cs @@ -1,413 +1,68 @@ -//Use project level define(s) when referencing with Paket. -//#define CSX_ENUM_INTERNAL // Uncomment this to set visibility to internal. -//#define CSX_ENUM_REM_STD_FUNC // Uncomment this to remove standard functions. -//#define CSX_REM_MAYBE_FUNC // Uncomment this to remove dependency to Maybe.cs. -//#define CSX_REM_EXTRA_FUNC // Uncomment this to extra functions. +// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information. using System; -using System.Collections; using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; using System.Linq; -using System.Text; -using LinqEnumerable = System.Linq.Enumerable; -namespace CSharpx +namespace CommandLine.Infrastructure { -#if !CSX_ENUM_INTERNAL - public -#endif static class EnumerableExtensions { -#if !CSX_ENUM_REM_STD_FUNC - private static IEnumerable AssertCountImpl(IEnumerable source, - int count, Func errorSelector) - { - var collection = source as ICollection; // Optimization for collections - if (collection != null) - { - if (collection.Count != count) - throw errorSelector(collection.Count.CompareTo(count), count); - return source; - } - - return ExpectingCountYieldingImpl(source, count, errorSelector); - } - - private static IEnumerable ExpectingCountYieldingImpl(IEnumerable source, - int count, Func errorSelector) - { - var iterations = 0; - foreach (var element in source) - { - iterations++; - if (iterations > count) - { - throw errorSelector(1, count); - } - yield return element; - } - if (iterations != count) - { - throw errorSelector(-1, count); - } - } - - /// - /// Returns the Cartesian product of two sequences by combining each element of the first set with each in the second - /// and applying the user=define projection to the pair. - /// - public static IEnumerable Cartesian(this IEnumerable first, IEnumerable second, Func resultSelector) - { - if (first == null) throw new ArgumentNullException("first"); - if (second == null) throw new ArgumentNullException("second"); - if (resultSelector == null) throw new ArgumentNullException("resultSelector"); - - return from item1 in first - from item2 in second // TODO buffer to avoid multiple enumerations - select resultSelector(item1, item2); - } - - /// - /// Prepends a single value to a sequence. - /// - public static IEnumerable Prepend(this IEnumerable source, TSource value) - { - if (source == null) throw new ArgumentNullException("source"); - - return LinqEnumerable.Concat(LinqEnumerable.Repeat(value, 1), source); - } - - /// - /// Returns a sequence consisting of the head element and the given tail elements. - /// - public static IEnumerable Concat(this T head, IEnumerable tail) - { - if (tail == null) throw new ArgumentNullException("tail"); - - return tail.Prepend(head); - } - - /// - /// Returns a sequence consisting of the head elements and the given tail element. - /// - public static IEnumerable Concat(this IEnumerable head, T tail) - { - if (head == null) throw new ArgumentNullException("head"); - - return LinqEnumerable.Concat(head, LinqEnumerable.Repeat(tail, 1)); - } - - /// - /// Excludes elements from a sequence starting at a given index - /// - /// The type of the elements of the sequence - public static IEnumerable Exclude(this IEnumerable sequence, int startIndex, int count) - { - if (sequence == null) throw new ArgumentNullException("sequence"); - if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex"); - if (count < 0) throw new ArgumentOutOfRangeException("count"); - - return ExcludeImpl(sequence, startIndex, count); - } - - private static IEnumerable ExcludeImpl(IEnumerable sequence, int startIndex, int count) + public static int IndexOf(this IEnumerable source, Func predicate) { var index = -1; - var endIndex = startIndex + count; - using (var iter = sequence.GetEnumerator()) - { - // yield the first part of the sequence - while (iter.MoveNext() && ++index < startIndex) - yield return iter.Current; - // skip the next part (up to count items) - while (++index < endIndex && iter.MoveNext()) - continue; - // yield the remainder of the sequence - while (iter.MoveNext()) - yield return iter.Current; - } - } - - /// - /// Returns a sequence of - /// where the key is the zero-based index of the value in the source - /// sequence. - /// - public static IEnumerable> Index(this IEnumerable source) - { - return source.Index(0); - } - - /// - /// Returns a sequence of - /// where the key is the index of the value in the source sequence. - /// An additional parameter specifies the starting index. - /// - public static IEnumerable> Index(this IEnumerable source, int startIndex) - { - return source.Select((item, index) => new KeyValuePair(startIndex + index, item)); - } - - /// - /// Returns the result of applying a function to a sequence of - /// 1 element. - /// - public static TResult Fold(this IEnumerable source, Func folder) - { - return FoldImpl(source, 1, folder, null, null, null); - } - - /// - /// Returns the result of applying a function to a sequence of - /// 2 elements. - /// - public static TResult Fold(this IEnumerable source, Func folder) - { - return FoldImpl(source, 2, null, folder, null, null); - } - - /// - /// Returns the result of applying a function to a sequence of - /// 3 elements. - /// - public static TResult Fold(this IEnumerable source, Func folder) - { - return FoldImpl(source, 3, null, null, folder, null); - } - - /// - /// Returns the result of applying a function to a sequence of - /// 4 elements. - /// - public static TResult Fold(this IEnumerable source, Func folder) - { - return FoldImpl(source, 4, null, null, null, folder); - } - - static TResult FoldImpl(IEnumerable source, int count, - Func folder1, - Func folder2, - Func folder3, - Func folder4) - { - if (source == null) throw new ArgumentNullException("source"); - if (count == 1 && folder1 == null - || count == 2 && folder2 == null - || count == 3 && folder3 == null - || count == 4 && folder4 == null) - { // ReSharper disable NotResolvedInText - throw new ArgumentNullException("folder"); // ReSharper restore NotResolvedInText - } - - var elements = new T[count]; - foreach (var e in AssertCountImpl(source.Index(), count, OnFolderSourceSizeErrorSelector)) - elements[e.Key] = e.Value; - - switch (count) + foreach (var item in source) { - case 1: return folder1(elements[0]); - case 2: return folder2(elements[0], elements[1]); - case 3: return folder3(elements[0], elements[1], elements[2]); - case 4: return folder4(elements[0], elements[1], elements[2], elements[3]); - default: throw new NotSupportedException(); - } - } - - static readonly Func OnFolderSourceSizeErrorSelector = OnFolderSourceSizeError; - - static Exception OnFolderSourceSizeError(int cmp, int count) - { - var message = cmp < 0 - ? "Sequence contains too few elements when exactly {0} {1} expected." - : "Sequence contains too many elements when exactly {0} {1} expected."; - return new Exception(string.Format(message, count.ToString("N0"), count == 1 ? "was" : "were")); - } - - /// - /// Immediately executes the given action on each element in the source sequence. - /// - /// The type of the elements in the sequence - public static void ForEach(this IEnumerable source, Action action) - { - if (source == null) throw new ArgumentNullException("source"); - if (action == null) throw new ArgumentNullException("action"); - - foreach (var element in source) - { - action(element); - } - } - - /// - /// Returns a sequence resulting from applying a function to each - /// element in the source sequence and its - /// predecessor, with the exception of the first element which is - /// only returned as the predecessor of the second element. - /// - public static IEnumerable Pairwise(this IEnumerable source, Func resultSelector) - { - if (source == null) throw new ArgumentNullException("source"); - if (resultSelector == null) throw new ArgumentNullException("resultSelector"); - - return PairwiseImpl(source, resultSelector); - } - - private static IEnumerable PairwiseImpl(this IEnumerable source, Func resultSelector) - { - Debug.Assert(source != null); - Debug.Assert(resultSelector != null); - - using (var e = source.GetEnumerator()) - { - if (!e.MoveNext()) - yield break; - - var previous = e.Current; - while (e.MoveNext()) + index++; + if (predicate(item)) { - yield return resultSelector(previous, e.Current); - previous = e.Current; + break; } } + return index; } - /// - /// Creates a delimited string from a sequence of values. The - /// delimiter used depends on the current culture of the executing thread. - /// - public static string ToDelimitedString(this IEnumerable source) - { - return ToDelimitedString(source, null); - } - - /// - /// Creates a delimited string from a sequence of values and - /// a given delimiter. - /// - public static string ToDelimitedString(this IEnumerable source, string delimiter) + public static object ToUntypedArray(this IEnumerable value, Type type) { - if (source == null) throw new ArgumentNullException("source"); - - return ToDelimitedStringImpl(source, delimiter, (sb, e) => sb.Append(e)); + var array = Array.CreateInstance(type, value.Count()); + value.ToArray().CopyTo(array, 0); + return array; } - static string ToDelimitedStringImpl(IEnumerable source, string delimiter, Func append) + public static bool Empty(this IEnumerable source) { - Debug.Assert(source != null); - Debug.Assert(append != null); - - delimiter = delimiter ?? CultureInfo.CurrentCulture.TextInfo.ListSeparator; - var sb = new StringBuilder(); - var i = 0; - - foreach (var value in source) - { - if (i++ > 0) sb.Append(delimiter); - append(sb, value); - } - - return sb.ToString(); + return !source.Any(); } -#endif -#if !CSX_REM_MAYBE_FUNC /// - /// Safe function that returns Just(first element) or None. + /// Breaks a collection into groups of a specified size. /// - public static Maybe TryHead(this IEnumerable source) + /// A collection of . + /// The number of items each group shall contain. + /// An enumeration of T[]. + /// An incomplete group at the end of the source collection will be silently dropped. + public static IEnumerable Group(this IEnumerable source, int groupSize) { - using (var e = source.GetEnumerator()) + if (groupSize < 1) { - return e.MoveNext() - ? Maybe.Just(e.Current) - : Maybe.Nothing(); + throw new ArgumentOutOfRangeException(nameof(groupSize)); } - } - /// - /// Turns an empty sequence to Nothing, otherwise Just(sequence). - /// - public static Maybe> ToMaybe(this IEnumerable source) - { - using (var e = source.GetEnumerator()) - { - return e.MoveNext() - ? Maybe.Just(source) - : Maybe.Nothing>(); - } - } -#endif - -#if !CSX_REM_EXTRA_FUNC - /// - /// Return everything except first element and throws exception if empty. - /// - public static IEnumerable Tail(this IEnumerable source) - { - using (var e = source.GetEnumerator()) - { - if (e.MoveNext()) - while (e.MoveNext()) - yield return e.Current; - else - throw new ArgumentException("Source sequence cannot be empty.", "source"); - } - } - - /// - /// Return everything except first element without throwing exception if empty. - /// - public static IEnumerable TailNoFail(this IEnumerable source) - { - using (var e = source.GetEnumerator()) - { - if (e.MoveNext()) - while (e.MoveNext()) - yield return e.Current; - } - } + T[] group = new T[groupSize]; + int groupIndex = 0; - /// - /// Captures current state of a sequence. - /// - public static IEnumerable Memorize(this IEnumerable source) - { - return source.GetType().IsArray ? source : source.ToArray(); - } - - /// - /// Creates an immutable copy of a sequence. - /// - public static IEnumerable Materialize(this IEnumerable source) - { - if (source is MaterializedEnumerable || source.GetType().IsArray) - { - return source; - } - return new MaterializedEnumerable(source); - } - - private class MaterializedEnumerable : IEnumerable - { - private readonly ICollection inner; - - public MaterializedEnumerable(IEnumerable enumerable) + foreach (var item in source) { - inner = enumerable as ICollection ?? enumerable.ToArray(); - } + group[groupIndex++] = item; - public IEnumerator GetEnumerator() - { - return inner.GetEnumerator(); - } + if (groupIndex == groupSize) + { + yield return group; - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); + group = new T[groupSize]; + groupIndex = 0; + } } } -#endif } } \ No newline at end of file diff --git a/src/CommandLine/Infrastructure/EnumerableExtensions`1.cs b/src/CommandLine/Infrastructure/EnumerableExtensions`1.cs deleted file mode 100644 index 056fa152..00000000 --- a/src/CommandLine/Infrastructure/EnumerableExtensions`1.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2005-2015 Giacomo Stelluti Scala & Contributors. All rights reserved. See License.md in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; - -namespace CommandLine.Infrastructure -{ - static class EnumerableExtensions - { - public static int IndexOf(this IEnumerable source, Func predicate) - { - var index = -1; - foreach (var item in source) - { - index++; - if (predicate(item)) - { - break; - } - } - return index; - } - - public static object ToUntypedArray(this IEnumerable value, Type type) - { - var array = Array.CreateInstance(type, value.Count()); - value.ToArray().CopyTo(array, 0); - return array; - } - - public static bool Empty(this IEnumerable source) - { - return !source.Any(); - } - - /// - /// Breaks a collection into groups of a specified size. - /// - /// A collection of . - /// The number of items each group shall contain. - /// An enumeration of T[]. - /// An incomplete group at the end of the source collection will be silently dropped. - public static IEnumerable Group(this IEnumerable source, int groupSize) - { - if (groupSize < 1) - { - throw new ArgumentOutOfRangeException(nameof(groupSize)); - } - - T[] group = new T[groupSize]; - int groupIndex = 0; - - foreach (var item in source) - { - group[groupIndex++] = item; - - if (groupIndex == groupSize) - { - yield return group; - - group = new T[groupSize]; - groupIndex = 0; - } - } - } - } -} \ No newline at end of file diff --git a/src/CommandLine/UnParserExtensions.cs b/src/CommandLine/UnParserExtensions.cs index 59c4d3b7..e06ebf56 100644 --- a/src/CommandLine/UnParserExtensions.cs +++ b/src/CommandLine/UnParserExtensions.cs @@ -136,7 +136,7 @@ public static string FormatCommandLine(this Parser parser, T options, Action< }) where !info.PropertyValue.IsEmpty(info.Specification, settings.SkipDefault) select info) - .Memorize(); + .Memoize(); var allOptSpecs = from info in specs.Where(i => i.Specification.Tag == SpecificationType.Option) let o = (OptionSpecification)info.Specification