/// <summary>
        /// Returns the constructed form of the provided generic type.
        /// </summary>
        public static IdSpan GetConstructed(this TypeConverter formatter, IdSpan unconstructed, params Type[] typeArguments)
        {
            var typeString     = unconstructed.AsSpan();
            var indicatorIndex = typeString.IndexOf((byte)GenericTypeIndicator);
            var arityString    = typeString.Slice(indicatorIndex + 1);

            if (indicatorIndex < 0 || arityString.IndexOf((byte)StartArgument) >= 0)
            {
                throw new InvalidOperationException("Cannot construct an already-constructed type");
            }

            if (!Utf8Parser.TryParse(arityString, out int arity, out var len) || len < arityString.Length || typeArguments.Length != arity)
            {
                throw new InvalidOperationException($"Insufficient number of type arguments, {typeArguments.Length}, provided while constructing type \"{unconstructed}\"");
            }

            var typeSpecs = new TypeSpec[typeArguments.Length];

            for (var i = 0; i < typeArguments.Length; i++)
            {
                typeSpecs[i] = RuntimeTypeNameParser.Parse(formatter.Format(typeArguments[i]));
            }

            var constructed = new ConstructedGenericTypeSpec(new NamedTypeSpec(null, unconstructed.ToStringUtf8(), typeArguments.Length), typeSpecs).Format();

            return(IdSpan.Create(constructed));
        }
        /// <summary>
        /// Returns the type arguments for the provided constructed generic type string.
        /// </summary>
        public static Type[] GetArguments(this TypeConverter formatter, IdSpan constructed)
        {
            var str   = constructed.AsSpan();
            var index = str.IndexOf((byte)StartArgument);

            if (index <= 0)
            {
                return(Array.Empty <Type>());
            }

            var safeString = "safer" + str.Slice(str.IndexOf((byte)GenericTypeIndicator)).GetUtf8String();
            var parsed     = RuntimeTypeNameParser.Parse(safeString);

            if (!(parsed is ConstructedGenericTypeSpec spec))
            {
                throw new InvalidOperationException($"Unable to correctly parse grain type {constructed}");
            }

            var result = new Type[spec.Arguments.Length];

            for (var i = 0; i < result.Length; i++)
            {
                var arg          = spec.Arguments[i];
                var formattedArg = arg.Format();
                result[i] = formatter.Parse(formattedArg);
                if (result[i] is null)
                {
                    throw new InvalidOperationException($"Unable to parse argument \"{formattedArg}\" as a type for grain type \"{constructed}\"");
                }
            }

            return(result);
        }
        private static (TypeSpec Type, string Assembly) HandleNamedType(NamedTypeSpec type, string assemblyName, Func <QualifiedType, QualifiedType> replaceTypeName)
        {
            var nsQualified = type.GetNamespaceQualifiedName();
            var names       = replaceTypeName(new QualifiedType(assembly: assemblyName, type: nsQualified));

            if (!string.Equals(nsQualified, names.Type, StringComparison.Ordinal))
            {
                // Change the type name and potentially the assembly.
                var resultType = RuntimeTypeNameParser.Parse(names.Type);
                if (!(resultType is NamedTypeSpec))
                {
                    throw new InvalidOperationException($"Replacement type name, \"{names.Type}\", can not deviate from the original type structure of the input, \"{nsQualified}\"");
                }

                return(resultType, names.Assembly);
            }
            else if (!string.Equals(assemblyName, names.Assembly, StringComparison.Ordinal))
            {
                // Only change the assembly;
                return(type, names.Assembly);
            }
            else if (type.ContainingType is object)
            {
                // Give the user an opportunity to change the parent, including the assembly.
                var replacementParent = ApplyInner(type.ContainingType, assemblyName, replaceTypeName);
                if (ReferenceEquals(replacementParent.Type, type.ContainingType))
                {
                    // No change to the type.
                    return(type, replacementParent.Assembly);
                }

                // The parent type changed.
                var typedReplacement = (NamedTypeSpec)replacementParent.Type;
                return(new NamedTypeSpec(typedReplacement, type.Name, type.Arity), replacementParent.Assembly);
            }
            else
            {
                return(type, names.Assembly);
            }
        }