using System; using System.Collections.Generic; using System.IO; using Unity.RenderStreaming.Signaling; using Unity.WebRTC; using UnityEngine; namespace Unity.RenderStreaming { [Serializable] internal struct CommandLineInfo { public string signalingType; public string signalingUrl; public RTCIceServer[] iceServers; public string pollingInterval; } static class CommandLineParser { internal static readonly StringArgument SignalingUrl = new StringArgument("-signalingUrl"); internal static readonly StringArgument SignalingType = new StringArgument("-signalingType"); internal static readonly StringArrayArgument IceServerUrls = new StringArrayArgument("-iceServerUrl"); internal static readonly StringArgument IceServerUsername = new StringArgument("-iceServerUsername"); internal static readonly StringArgument IceServerCredential = new StringArgument("-iceServerCredential"); internal static readonly EnumArgument IceServerCredentialType = new EnumArgument("-iceServerCredentialType"); internal static readonly IntArgument PollingInterval = new IntArgument("-pollingInterval"); internal static readonly JsonFileArgument ImportJson = new JsonFileArgument("-importJson"); static readonly List options = new List() { SignalingUrl, SignalingType, IceServerUrls, IceServerUsername, IceServerCredential, IceServerCredentialType, PollingInterval, ImportJson }; internal delegate bool TryParseDelegate(string[] arguments, string argumentName, out T result); internal interface IArgument { bool TryParse(string[] arguments); } internal abstract class BaseArgument : IArgument { /// /// A switch that will either retrieve the argument using the resolver, or use the readonly /// argument name string set on construction. /// public string ArgumentName { get; } /// /// /// public bool Defined => m_defined; /// /// /// public readonly bool Required; /// /// /// public T Value => m_value; protected bool m_defined; protected T m_value; readonly TryParseDelegate m_parser; protected abstract bool DefaultParser(string[] arguments, string argumentName, out T parsedResult); public bool TryParse(string[] arguments) { m_defined = m_parser != null && m_parser(arguments, ArgumentName, out m_value); return m_defined; } internal BaseArgument(string argumentName, bool required = false) { Required = required; ArgumentName = argumentName; m_parser = DefaultParser; } internal BaseArgument(string argumentName, TryParseDelegate tryParseDelegate, bool required = false) { Required = required; ArgumentName = argumentName; m_parser = tryParseDelegate; } } internal class StringArgument : BaseArgument { protected override bool DefaultParser(string[] arguments, string argumentName, out string parsedResult) => TryParseStringArgument(arguments, argumentName, out parsedResult, Required); internal StringArgument(string argumentName, bool required = false) : base(argumentName, required) { } internal StringArgument(string argumentName, TryParseDelegate tryParse, bool required = false) : base(argumentName, tryParse, required) { } public static implicit operator string(StringArgument argument) => !argument.Defined ? null : argument.Value; } internal class EnumArgument : BaseArgument where T : struct { protected override bool DefaultParser(string[] arguments, string argumentName, out T? parsedResult) => TryParseEnumArgument(arguments, argumentName, out parsedResult, Required); internal EnumArgument(string argumentName, bool required = false) : base(argumentName, required) { } public static implicit operator T?(EnumArgument argument) => !argument.Defined ? null : argument.Value; } internal class StringArrayArgument : BaseArgument { protected override bool DefaultParser(string[] arguments, string argumentName, out string[] parsedResult) => TryParseStringArrayArgument(arguments, argumentName, out parsedResult, Required); internal StringArrayArgument(string argumentName, bool required = false) : base(argumentName, required) { } public static implicit operator string[](StringArrayArgument argument) => !argument.Defined ? null : argument.Value; } internal class IntArgument : BaseArgument { protected override bool DefaultParser(string[] arguments, string argumentName, out int? parsedResult) => TryParseIntArgument(arguments, argumentName, out parsedResult, Required); internal IntArgument(string argumentName, bool required = false) : base(argumentName, required) { } public static implicit operator int?(IntArgument argument) => !argument.Defined ? null : argument.Value; } internal class JsonFileArgument : BaseArgument where T : struct { protected override bool DefaultParser(string[] arguments, string argumentName, out T? parsedResult) => TryParseJsonFileArgument(arguments, argumentName, out parsedResult, Required); internal JsonFileArgument(string argumentName, bool required = false) : base(argumentName, required) { } public static implicit operator T?(JsonFileArgument argument) => !argument.Defined ? null : argument.Value; } static bool TryParseStringArgument(string[] arguments, string argumentName, out string argumentValue, bool required = false) { var startIndex = System.Array.FindIndex(arguments, x => x == argumentName); if (startIndex < 0) { argumentValue = null; return !required; } if (startIndex + 1 >= arguments.Length) { argumentValue = null; return false; } argumentValue = arguments[startIndex + 1]; return !string.IsNullOrEmpty(argumentValue); } static bool TryParseEnumArgument(string[] arguments, string argumentName, out T? argumentValue, bool required = false) where T : struct { bool result = TryParseStringArgument(arguments, argumentName, out string value, required); if (result && !string.IsNullOrEmpty(value)) { if (Enum.TryParse(value, true, out T enumValue)) { argumentValue = enumValue; return true; } result = false; } argumentValue = null; return result; } static bool TryParseIntArgument(string[] arguments, string argumentName, out int? argumentValue, bool required = false) { var startIndex = System.Array.FindIndex(arguments, x => x == argumentName); if (startIndex < 0) { argumentValue = null; return !required; } if (startIndex + 1 >= arguments.Length) { argumentValue = null; return false; } if (!int.TryParse(arguments[startIndex + 1], out var result)) { argumentValue = null; return false; } argumentValue = result; return true; } static bool TryParseStringArrayArgument(string[] arguments, string argumentName, out string[] argumentValue, bool required = false) { List list = new List(); for (int i = 0; i < arguments.Length;) { var startIndex = Array.FindIndex(arguments, i, x => x == argumentName); if (startIndex < 0) break; if (startIndex + 1 >= arguments.Length) break; list.Add(arguments[startIndex + 1]); i = startIndex + 2; } if (list.Count == 0 && required) { argumentValue = null; return false; } argumentValue = list.ToArray(); return true; } static bool TryParseJsonFileArgument(string[] arguments, string argumentName, out T? argumentValue, bool required = false) where T : struct { bool result = TryParseFilePathArgument(arguments, argumentName, out string value); if (result && !string.IsNullOrEmpty(value)) { string text = File.ReadAllText(value); try { argumentValue = JsonUtility.FromJson(text); return true; } catch (Exception) { result = false; } } else if (required) result = false; argumentValue = null; return result; } static bool TryParseFilePathArgument(string[] arguments, string argumentName, out string argumentValue) { bool ret = TryParseStringArgument(arguments, argumentName, out string value); if (!ret) { argumentValue = null; return false; } if (string.IsNullOrEmpty(value)) { argumentValue = null; return true; } if (!File.Exists(value)) { argumentValue = null; return false; } argumentValue = value; return true; } internal static bool TryParse(string[] arguments) { foreach (var option in options) { if (!option.TryParse(arguments)) return false; } return true; } } }