146 lines
5.4 KiB
C#
146 lines
5.4 KiB
C#
using System.Linq;
|
|
using System.Reflection;
|
|
using Unosquare.Swan.Attributes;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Unosquare.Swan.Components {
|
|
/// <summary>
|
|
/// Provides methods to parse command line arguments.
|
|
/// Based on CommandLine (Copyright 2005-2015 Giacomo Stelluti Scala and Contributors.).
|
|
/// </summary>
|
|
public partial class ArgumentParser {
|
|
private sealed class Validator {
|
|
private readonly Object _instance;
|
|
private readonly IEnumerable<String> _args;
|
|
private readonly List<PropertyInfo> _updatedList = new List<PropertyInfo>();
|
|
private readonly ArgumentParserSettings _settings;
|
|
|
|
private readonly PropertyInfo[] _properties;
|
|
|
|
public Validator(
|
|
PropertyInfo[] properties,
|
|
IEnumerable<String> args,
|
|
Object instance,
|
|
ArgumentParserSettings settings) {
|
|
this._args = args;
|
|
this._instance = instance;
|
|
this._settings = settings;
|
|
this._properties = properties;
|
|
|
|
this.PopulateInstance();
|
|
this.SetDefaultValues();
|
|
this.GetRequiredList();
|
|
}
|
|
|
|
public List<String> UnknownList { get; } = new List<String>();
|
|
public List<String> RequiredList { get; } = new List<String>();
|
|
|
|
public Boolean IsValid() => (this._settings.IgnoreUnknownArguments || !this.UnknownList.Any()) && !this.RequiredList.Any();
|
|
|
|
public IEnumerable<ArgumentOptionAttribute> GetPropertiesOptions()
|
|
=> this._properties.Select(p => Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p))
|
|
.Where(x => x != null);
|
|
|
|
private void GetRequiredList() {
|
|
foreach(PropertyInfo targetProperty in this._properties) {
|
|
ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
|
|
|
if(optionAttr == null || optionAttr.Required == false) {
|
|
continue;
|
|
}
|
|
|
|
if(targetProperty.GetValue(this._instance) == null) {
|
|
this.RequiredList.Add(optionAttr.LongName ?? optionAttr.ShortName);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SetDefaultValues() {
|
|
foreach(PropertyInfo targetProperty in this._properties.Except(this._updatedList)) {
|
|
ArgumentOptionAttribute optionAttr = Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(targetProperty);
|
|
|
|
Object defaultValue = optionAttr?.DefaultValue;
|
|
|
|
if(defaultValue == null) {
|
|
continue;
|
|
}
|
|
|
|
if(this.SetPropertyValue(targetProperty, defaultValue.ToString(), this._instance, optionAttr)) {
|
|
this._updatedList.Add(targetProperty);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PopulateInstance() {
|
|
const Char dash = '-';
|
|
String propertyName = String.Empty;
|
|
|
|
foreach(String arg in this._args) {
|
|
Boolean ignoreSetValue = String.IsNullOrWhiteSpace(propertyName);
|
|
|
|
if(ignoreSetValue) {
|
|
if(String.IsNullOrWhiteSpace(arg) || arg[0] != dash) {
|
|
continue;
|
|
}
|
|
|
|
propertyName = arg.Substring(1);
|
|
|
|
if(!String.IsNullOrWhiteSpace(propertyName) && propertyName[0] == dash) {
|
|
propertyName = propertyName.Substring(1);
|
|
}
|
|
}
|
|
|
|
PropertyInfo targetProperty = this.TryGetProperty(propertyName);
|
|
|
|
if(targetProperty == null) {
|
|
// Skip if the property is not found
|
|
this.UnknownList.Add(propertyName);
|
|
continue;
|
|
}
|
|
|
|
if(!ignoreSetValue && this.SetPropertyValue(targetProperty, arg, this._instance)) {
|
|
this._updatedList.Add(targetProperty);
|
|
propertyName = String.Empty;
|
|
} else if(targetProperty.PropertyType == typeof(Boolean)) {
|
|
// If the arg is a boolean property set it to true.
|
|
targetProperty.SetValue(this._instance, true);
|
|
|
|
this._updatedList.Add(targetProperty);
|
|
propertyName = String.Empty;
|
|
}
|
|
}
|
|
|
|
if(!String.IsNullOrEmpty(propertyName)) {
|
|
this.UnknownList.Add(propertyName);
|
|
}
|
|
}
|
|
|
|
private Boolean SetPropertyValue(
|
|
PropertyInfo targetProperty,
|
|
String propertyValueString,
|
|
Object result,
|
|
ArgumentOptionAttribute optionAttr = null) {
|
|
if(targetProperty.PropertyType.GetTypeInfo().IsEnum) {
|
|
Object parsedValue = Enum.Parse(
|
|
targetProperty.PropertyType,
|
|
propertyValueString,
|
|
this._settings.CaseInsensitiveEnumValues);
|
|
|
|
targetProperty.SetValue(result, Enum.ToObject(targetProperty.PropertyType, parsedValue));
|
|
|
|
return true;
|
|
}
|
|
|
|
return targetProperty.PropertyType.IsArray
|
|
? targetProperty.TrySetArray(propertyValueString.Split(optionAttr?.Separator ?? ','), result)
|
|
: targetProperty.TrySetBasicType(propertyValueString, result);
|
|
}
|
|
|
|
private PropertyInfo TryGetProperty(String propertyName)
|
|
=> this._properties.FirstOrDefault(p =>
|
|
String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.LongName, propertyName, this._settings.NameComparer) ||
|
|
String.Equals(Runtime.AttributeCache.RetrieveOne<ArgumentOptionAttribute>(p)?.ShortName, propertyName, this._settings.NameComparer));
|
|
}
|
|
}
|
|
} |