Skip to content

Commit

Permalink
Nullability
Browse files Browse the repository at this point in the history
  • Loading branch information
wallymathieu committed Jun 7, 2024
1 parent 29019c7 commit 1e1e7c5
Show file tree
Hide file tree
Showing 45 changed files with 187 additions and 171 deletions.
2 changes: 2 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
<PackageProjectUrl>https://github.com/wallymathieu/isop</PackageProjectUrl>
<PackageTags>CLI;command line;commandline;argument;option;parser;parsing;library;syntax;shell</PackageTags>
<LangVersion>12.0</LangVersion>
<Nullable>enable</Nullable>
<WarningsAsErrors>Nullable</WarningsAsErrors>
</PropertyGroup>

<!-- mono -->
Expand Down
2 changes: 1 addition & 1 deletion src/Example/MyController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public string ActionWithGlobalParameter(string global)
}
public class Argument
{
public string MyProperty { get; set; }
public string? MyProperty { get; set; }

}
public string ActionWithObjectArgument(Argument arg)
Expand Down
2 changes: 1 addition & 1 deletion src/FullExample/MyController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public string ActionWithGlobalParameter(string global)
}
public class Argument
{
public string MyProperty { get; set; }
public string? MyProperty { get; set; }

}
public string ActionWithObjectArgument(Argument arg)
Expand Down
2 changes: 1 addition & 1 deletion src/Isop/Abstractions/ArgumentAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ namespace Isop.Abstractions
/// <summary>
/// Action that should be invoked when argument is encountered
/// </summary>
public delegate Task<object> ArgumentAction(string value);
public delegate Task<object?> ArgumentAction(string? value);
}
2 changes: 1 addition & 1 deletion src/Isop/Abstractions/IActionOnController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public interface IActionOnController
/// <summary>
/// send parameters to controller actions
/// </summary>
IParsed Parameters(Dictionary<string, string> parameters);
IParsed Parameters(Dictionary<string, string?> parameters);

/// <summary>
/// Get help for controller action
Expand Down
4 changes: 2 additions & 2 deletions src/Isop/Abstractions/IAppHostBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public interface IAppHostBuilder
/// <param name="required"></param>
/// <param name="description"></param>
/// <returns></returns>
IAppHostBuilder Parameter(string argument, ArgumentAction action = null, bool required = false, string description = null);
IAppHostBuilder Parameter(string argument, ArgumentAction? action = null, bool required = false, string? description = null);

/// <summary>
///
Expand All @@ -35,7 +35,7 @@ public interface IAppHostBuilder
/// <param name="required"></param>
/// <param name="description"></param>
/// <returns></returns>
IAppHostBuilder Parameter(string argument, Action<string> action, bool required = false, string description = null);
IAppHostBuilder Parameter(string argument, Action<string?> action, bool required = false, string? description = null);

/// <summary>
///
Expand Down
12 changes: 6 additions & 6 deletions src/Isop/Abstractions/InvokeResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@ public class Empty : InvokeResult
{
}

public class ControllerAction(object result) : InvokeResult
public class ControllerAction(object? result) : InvokeResult
{
public object Result { get; } = result;
public object? Result { get; } = result;
}

public class Argument(object result) : InvokeResult
public class Argument(object? result) : InvokeResult
{
public object Result { get; } = result;
public object? Result { get; } = result;
}

public class AsyncControllerAction(Task<object> task) : InvokeResult
public class AsyncControllerAction(Task<object?> task) : InvokeResult
{
public Task<object> Task { get; } = task;
public Task<object?> Task { get; } = task;
}

/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Isop/Abstractions/ToStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace Isop.Abstractions
/// <summary>
/// Do a ToString conversion to format a value as a string. To format enumerable as multiple strings.
/// </summary>
public delegate IEnumerable<string> ToStrings(object value);
public delegate IEnumerable<string> ToStrings(object? value);
}

2 changes: 1 addition & 1 deletion src/Isop/Abstractions/TypeConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ namespace Isop.Abstractions
/// <summary>
/// Represents a type converter
/// </summary>
public delegate object TypeConverter(System.Type type, string value, System.Globalization.CultureInfo culture);
public delegate object? TypeConverter(System.Type type, string? value, System.Globalization.CultureInfo? culture);
}
6 changes: 3 additions & 3 deletions src/Isop/CommandLine/Argument.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
namespace Isop.CommandLine
{
using Parse;
public sealed class Argument(ArgumentParameter parameter, bool required = false, string description = null)
public sealed class Argument(ArgumentParameter parameter, bool required = false, string? description = null)
{
public ArgumentParameter Parameter { get; } = parameter;

public string Description { get; } = description;
public string? Description { get; } = description;
public string Name => Parameter.LongAlias();
public bool Required { get; } = required;

public override bool Equals(object obj)
public override bool Equals(object? obj)
{
if (obj is null) return false;
if (ReferenceEquals(this, obj)) return true;
Expand Down
5 changes: 3 additions & 2 deletions src/Isop/CommandLine/ArgumentInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ namespace Isop.CommandLine
using Infrastructure;
using System.Threading;
using System.Reflection;
using System.Diagnostics.CodeAnalysis;

public class ArgumentInvoker(IServiceProvider serviceProvider, Recognizes recognizes, HelpController helpController)
{
private ILookup<string, ArgumentAction>? _recognizesMap;

private ILookup<string,ArgumentAction> RecognizesMap =>
_recognizesMap ??= recognizes.Properties.Where(p=>p.Action!=null).ToLookup(p=>p.Name, p=>p.Action);
_recognizesMap ??= recognizes.Properties.Where(p=>p.Action!=null).ToLookup(p=>p.Name, p=>p.Action!);

#if NET8_0_OR_GREATER
private static bool IsIAsyncEnumerable(Type type) =>(
Expand All @@ -36,7 +37,7 @@ private static bool IsIAsyncEnumerable(Type type) =>(
yield return val;
}
}
private static bool AsyncEnumerable(object result, out IAsyncEnumerable<object?>? enumerable)
private static bool AsyncEnumerable(object? result, [NotNullWhen(true)] out IAsyncEnumerable<object?>? enumerable)
{
enumerable = null;
if (result is null) return false;
Expand Down
13 changes: 9 additions & 4 deletions src/Isop/CommandLine/ControllerRecognizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Globalization;
using System.Linq;
using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis;

namespace Isop.CommandLine
{
Expand Down Expand Up @@ -94,20 +95,24 @@ public ParsedArguments Parse(Controller controller, Method method, IReadOnlyColl

if (! _convertArgument.TryGetParametersForMethod(method,
parsedArguments.Recognized
.Select(a => new KeyValuePair<string,string>(a.RawArgument, a.Value))
.Select(a => new KeyValuePair<string,string?>(a.RawArgument, a.Value))
.ToArray(), out var recognizedActionParameters, out var missingParameters))
return new ParsedArguments.MethodMissingArguments(
missingParameters: missingParameters,
missingParameters: missingParameters!,
recognizedClass: controller.Type,
recognizedAction: method);
return new ParsedArguments.Method(
recognizedActionParameters: recognizedActionParameters,
recognizedActionParameters: recognizedActionParameters!,
recognized: parsedArguments.Recognized,
recognizedClass: controller.Type,
recognizedAction: method);
}

public bool TryFindController(string name, out Controller controller)
public bool TryFindController(string name,
#if NET8_0_OR_GREATER
[NotNullWhen(true)]
#endif
out Controller? controller)
{
if (_controllerActionMap.TryGetValue(name,
out var controllerAndMap))
Expand Down
26 changes: 18 additions & 8 deletions src/Isop/CommandLine/ConvertArgumentsToParameterValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.Globalization;
using System.Linq;
using Microsoft.Extensions.Options;

using System.Diagnostics.CodeAnalysis;
namespace Isop.CommandLine
{
using Abstractions;
Expand All @@ -17,10 +17,18 @@ internal class ConvertArgumentsToParameterValue(
private readonly CultureInfo? _culture = configuration?.Value.CultureInfo;

public bool TryGetParametersForMethod(Method method,
IReadOnlyCollection<KeyValuePair<string,string>> parsedArguments, out IReadOnlyCollection<object> parameters, out IReadOnlyCollection<string> missingParameters)
IReadOnlyCollection<KeyValuePair<string,string?>> parsedArguments,
#if NET8_0_OR_GREATER
[NotNullWhen(true)]
#endif
out IReadOnlyCollection<object?>? parameters,
#if NET8_0_OR_GREATER
[NotNullWhen(false)]
#endif
out IReadOnlyCollection<string>? missingParameters)
{
var parameterInfos = method.GetParameters();
var parameterValues = new List<object>();
var parameterValues = new List<object?>();
var missing = new List<string>();
foreach (var paramInfo in parameterInfos)
{
Expand All @@ -44,14 +52,16 @@ public bool TryGetParametersForMethod(Method method,
}
}

parameters = missing.Any() ? null: parameterValues;
missingParameters = missing.Any() ? missing : null;
return !missing.Any();
bool anythingMissing = missing.Count != 0;
parameters = anythingMissing ? null: parameterValues;
missingParameters = anythingMissing ? missing : null;
return !anythingMissing;
}

private object CreateObjectFromArguments(IReadOnlyCollection<KeyValuePair<string,string>> parsedArguments, Parameter paramInfo)
private object CreateObjectFromArguments(IReadOnlyCollection<KeyValuePair<string,string?>> parsedArguments, Parameter paramInfo)
{
var obj = Activator.CreateInstance(paramInfo.ParameterType);
if (obj is null) throw new Exception($"Failed to initialize {paramInfo.ParameterType.Name}");
foreach (var prop in paramInfo.GetPublicInstanceProperties())
{
var recognizedArgument = parsedArguments.FirstOrDefault(a => a.Key.EqualsIgnoreCase(prop.Name));
Expand All @@ -63,7 +73,7 @@ private object CreateObjectFromArguments(IReadOnlyCollection<KeyValuePair<string
return obj;
}

private object ConvertFrom(KeyValuePair<string,string> arg1, Type type)
private object? ConvertFrom(KeyValuePair<string,string?> arg1, Type type)
{
try
{
Expand Down
4 changes: 2 additions & 2 deletions src/Isop/CommandLine/Help/HelpForArgumentWithOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ public string Help(string? val = null)
return _texts.TheArgumentsAre + Environment.NewLine +
string.Join(Environment.NewLine,
recognizes.Properties.Select(ar => " "+ Help(ar)).ToArray());
return Help(recognizes.Properties.First(ar => ar.ToArgument(config?.Value?.CultureInfo).Accept(val)));
return Help(recognizes.Properties.First(ar => ar.ToArgument(config?.Value?.CultureInfo).Accept(val!)));

Check warning on line 29 in src/Isop/CommandLine/Help/HelpForArgumentWithOptions.cs

View workflow job for this annotation

GitHub Actions / build

'PropertyExtensions.ToArgument(Property, IFormatProvider?)' is obsolete: 'Temporary'

Check warning on line 29 in src/Isop/CommandLine/Help/HelpForArgumentWithOptions.cs

View workflow job for this annotation

GitHub Actions / build

'PropertyExtensions.ToArgument(Property, IFormatProvider?)' is obsolete: 'Temporary'
}

public bool CanHelp(string? val = null)
{
return string.IsNullOrEmpty(val)
? recognizes.Properties.Any()
: recognizes.Properties.Any(ar => ar.ToArgument(config?.Value?.CultureInfo).Accept(val));
: recognizes.Properties.Any(ar => ar.ToArgument(config?.Value?.CultureInfo).Accept(val!));

Check warning on line 36 in src/Isop/CommandLine/Help/HelpForArgumentWithOptions.cs

View workflow job for this annotation

GitHub Actions / build

'PropertyExtensions.ToArgument(Property, IFormatProvider?)' is obsolete: 'Temporary'

Check warning on line 36 in src/Isop/CommandLine/Help/HelpForArgumentWithOptions.cs

View workflow job for this annotation

GitHub Actions / build

'PropertyExtensions.ToArgument(Property, IFormatProvider?)' is obsolete: 'Temporary'
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Isop/CommandLine/Lex/Token.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public struct Token(string value, TokenType tokenType, int index)
/// </summary>
public int Index = index;

public override readonly bool Equals(object obj)
public override readonly bool Equals(object? obj)
{
if (obj is null) return false;
return obj is Token token && Equals(token);
Expand Down
2 changes: 1 addition & 1 deletion src/Isop/CommandLine/Parse/ArgumentParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public bool Accept(int index, string val)
public string LongAlias()
{
var maxLength = Aliases.Max(a => a.Length);
return Aliases.SingleOrDefault(a => a.Length == maxLength);
return Aliases.SingleOrDefault(a => a.Length == maxLength) ?? throw new NullReferenceException("alias");
}

public bool Accept(string value)
Expand Down
10 changes: 5 additions & 5 deletions src/Isop/CommandLine/Parse/ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public ParsedArguments.Properties Parse(IReadOnlyList<Token> lexed, IReadOnlyCol
!recognized.Any(otherArgument =>
argument.InferredOrdinal &&
!ReferenceEquals(argument, otherArgument)
&& otherArgument.RawArgument.Equals(argument.RawArgument)))
&& (otherArgument.RawArgument?.Equals(argument.RawArgument)??false)))
.ToList();
var recognizedIndexes = minusDuplicates.SelectMany(token=>token.Index).ToList();

Expand All @@ -112,10 +112,10 @@ private void InferParameter(IList<RecognizedArgument> recognized, Token current)
if (null != argumentWithOptions)
{
recognized.Add(new RecognizedArgument(
argumentWithOptions,
[current.Index],
argumentWithOptions.Name,
current.Value,
argument: argumentWithOptions,
index: [current.Index],
rawArgument: argumentWithOptions.Name,
value: current.Value,
inferredOrdinal:true));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Isop/CommandLine/PropertyExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Isop.CommandLine
internal static class PropertyExtensions
{
[Obsolete("Temporary")]
public static Argument ToArgument(this Property p, IFormatProvider formatProvider)
public static Argument ToArgument(this Property p, IFormatProvider? formatProvider)
{
var argumentParam= ArgumentParameter.Parse(p.Name, formatProvider);
return new Argument(
Expand Down
6 changes: 3 additions & 3 deletions src/Isop/CommandLine/ToStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Isop.CommandLine
{
internal static class ToStrings
{
public static IEnumerable<string> Default(object value)
public static IEnumerable<string> Default(object? value)
{
if (value == null) yield break;
switch (value)
Expand All @@ -28,12 +28,12 @@ public static IEnumerable<string> Default(object value)
break;
}
default:
yield return value.ToString();
yield return value.ToString()!;
break;
}
}

public static IEnumerable<string> AsTable(object value)
public static IEnumerable<string> AsTable(object? value)
{
if (value == null) yield break;
switch (value)
Expand Down
4 changes: 2 additions & 2 deletions src/Isop/Domain/Controller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ private static IEnumerable<MethodInfo> GetOwnPublicMethods(Type type) =>
.Where(m => !m.Name.StartsWithIgnoreCase("get_")
&& !m.Name.StartsWithIgnoreCase("set_"));

public Method GetMethod(Conventions conventions, string name) =>
public Method? GetMethod(Conventions conventions, string name) =>
GetControllerActionMethods(conventions).SingleOrDefault(m => m.Name.EqualsIgnoreCase(name));

public bool IsHelp() => Type == typeof(HelpController);

public override bool Equals(object obj) => obj is Controller controller && Equals(controller);
public override bool Equals(object? obj) => obj is Controller controller && Equals(controller);
public bool Equals(Controller obj) => Type == obj?.Type;

public override int GetHashCode() => Type.GetHashCode();
Expand Down
16 changes: 9 additions & 7 deletions src/Isop/Domain/Method.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ public class Method(MethodInfo methodInfo)
public string Name => methodInfo.Name;
public MethodInfo MethodInfo => methodInfo;
public Parameter[] GetParameters() => methodInfo.GetParameters().Select(p => new Parameter(p)).ToArray();
public object Invoke(object instance, object[] values)
public object? Invoke(object instance, object[] values)
{
if (instance==null) throw new ArgumentNullException(nameof(instance));
if (instance == null) throw new ArgumentNullException(nameof(instance));
try
{
return methodInfo.Invoke(instance, values);
}catch(TargetInvocationException ex){
}
catch (TargetInvocationException ex)
{
if (ex.InnerException != null)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
Expand Down Expand Up @@ -52,14 +54,14 @@ private static Argument CreateArgument(Parameter parameterInfo, IFormatProvider?
new Argument(required: parameterInfo.LooksRequired(),
parameter: ArgumentParameter.Parse(parameterInfo.Name, cultureInfo));

private static IEnumerable<Argument> CreateArgumentsForInstanceProperties(Parameter parameterInfo, IFormatProvider cultureInfo) =>
private static IEnumerable<Argument> CreateArgumentsForInstanceProperties(Parameter parameterInfo, IFormatProvider? cultureInfo) =>
parameterInfo.GetPublicInstanceProperties()
.Select(prop =>
new Argument(
.Select(prop =>
new Argument(
required: parameterInfo.LooksRequired() && IsRequired(prop),
parameter: ArgumentParameter.Parse(prop.Name, cultureInfo)));

private static bool IsRequired(PropertyInfo propertyInfo) =>
private static bool IsRequired(PropertyInfo propertyInfo) =>
propertyInfo.GetCustomAttributes(typeof(RequiredAttribute), true).Any();
}
}
Expand Down
Loading

0 comments on commit 1e1e7c5

Please sign in to comment.