Example #1
0
        /*********
        ** Public methods
        *********/
        /// <summary>Get an immutable set for the given values.</summary>
        /// <param name="values">The values for which to get a set.</param>
        public static IInvariantSet From(IEnumerable <string> values)
        {
            // shortcut predefined values
            switch (values)
            {
            // already an invariant set
            case IInvariantSet set:
                return(set);

            // use predefined set if possible
            case IList <string> list:
                switch (list.Count)
                {
                case 0:
                    return(InvariantSets.Empty);

                case 1:
                    return(InvariantSets.FromValue(list[0]));

                case 2 when bool.TryParse(list[0], out bool left) && bool.TryParse(list[1], out bool right):
                    if (left != right)
                    {
                        return(InvariantSets.Boolean);
                    }
                    return(left
                                ? InvariantSets.True
                                : InvariantSets.False);
                }
                break;
            }

            // create custom set
            IInvariantSet result = values is MutableInvariantSet mutableSet
                ? mutableSet.GetImmutable()
                : new InvariantSet(values);

            return(result.Count == 0
                ? InvariantSets.Empty
                : result);
        }
        /// <summary>Get unique, trimmed, comma-separated values from a token string.</summary>
        /// <param name="tokenStr">The token string to parse.</param>
        /// <param name="normalize">Normalize a value.</param>
        /// <exception cref="InvalidOperationException">The token string is not ready (<see cref="IContextual.IsReady"/> is false).</exception>
        public static IInvariantSet SplitValuesUnique(this ITokenString?tokenStr, Func <string, string>?normalize = null)
        {
            if (tokenStr?.IsReady is false)
            {
                throw new InvalidOperationException($"Can't get values from a non-ready token string (raw value: {tokenStr.Raw}).");
            }

            if (string.IsNullOrWhiteSpace(tokenStr?.Value))
            {
                return(InvariantSets.Empty);
            }

            string[] values = tokenStr.Value.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            if (normalize != null)
            {
                for (int i = 0; i < values.Length; i++)
                {
                    values[i] = normalize(values[i]);
                }
            }

            return(InvariantSets.From(values));
        }
        /*********
        ** Private methods
        *********/
        /// <summary>Parse a raw config schema for a content pack.</summary>
        /// <param name="rawSchema">The raw config schema.</param>
        /// <param name="logWarning">The callback to invoke on each validation warning, passed the field name and reason respectively.</param>
        /// <param name="formatVersion">The content format version.</param>
        private InvariantDictionary <ConfigField> LoadConfigSchema(InvariantDictionary <ConfigSchemaFieldConfig?>?rawSchema, Action <string, string> logWarning, ISemanticVersion formatVersion)
        {
            InvariantDictionary <ConfigField> schema = new();

            if (rawSchema == null || !rawSchema.Any())
            {
                return(schema);
            }

            foreach (string rawKey in rawSchema.Keys)
            {
                ConfigSchemaFieldConfig?field = rawSchema[rawKey];
                if (field is null)
                {
                    continue;
                }

                // validate format
                if (string.IsNullOrWhiteSpace(rawKey))
                {
                    logWarning(rawKey, "the config field name can't be empty.");
                    continue;
                }
                if (rawKey.Contains(InternalConstants.PositionalInputArgSeparator) || rawKey.Contains(InternalConstants.NamedInputArgSeparator))
                {
                    logWarning(rawKey, $"the name '{rawKey}' can't have input arguments ({InternalConstants.PositionalInputArgSeparator} or {InternalConstants.NamedInputArgSeparator} character).");
                    continue;
                }

                // validate reserved keys
                if (Enum.TryParse <ConditionType>(rawKey, true, out _))
                {
                    logWarning(rawKey, $"can't use {rawKey} as a config field, because it's a reserved condition key.");
                    continue;
                }

                // read allowed/default values
                IInvariantSet allowValues   = this.ParseCommaDelimitedField(field.AllowValues);
                IInvariantSet defaultValues = this.ParseCommaDelimitedField(field.Default);

                // pre-1.7 behaviour
                if (formatVersion.IsOlderThan("1.7"))
                {
                    // allowed values are required
                    if (!allowValues.Any())
                    {
                        logWarning(rawKey, $"no {nameof(ConfigSchemaFieldConfig.AllowValues)} specified (and format version is less than 1.7).");
                        continue;
                    }

                    // inject default if needed
                    if (!defaultValues.Any() && !field.AllowBlank)
                    {
                        defaultValues = InvariantSets.FromValue(allowValues.First());
                    }
                }

                // validate allowed values
                if (!field.AllowBlank && !defaultValues.Any())
                {
                    logWarning(rawKey, $"if {nameof(field.AllowBlank)} is false, you must specify {nameof(field.Default)}.");
                    continue;
                }
                if (allowValues.Any() && defaultValues.Any())
                {
                    IInvariantSet invalidValues = defaultValues.GetWithout(allowValues);
                    if (invalidValues.Any())
                    {
                        logWarning(rawKey, $"default values '{string.Join(", ", invalidValues)}' are not allowed according to {nameof(ConfigSchemaFieldConfig.AllowValues)}.");
                        continue;
                    }
                }

                // validate allow multiple
                if (!field.AllowMultiple && defaultValues.Count > 1)
                {
                    logWarning(rawKey, $"can't have multiple default values because {nameof(ConfigSchemaFieldConfig.AllowMultiple)} is false.");
                    continue;
                }

                // add to schema
                schema[rawKey] = new ConfigField(
                    allowValues: allowValues,
                    defaultValues: defaultValues,
                    value: InvariantSets.Empty,
                    allowBlank: field.AllowBlank,
                    allowMultiple: field.AllowMultiple,
                    description: field.Description,
                    section: field.Section
                    );
            }

            return(schema);
        }