/// <summary>Parse a string which can contain tokens, and validate that it's valid.</summary> /// <param name="rawValue">The raw string which may contain tokens.</param> /// <param name="tokenContext">The tokens available for this content pack.</param> /// <param name="error">An error phrase indicating why parsing failed (if applicable).</param> /// <param name="parsed">The parsed value.</param> private bool TryParseTokenString(string rawValue, IContext tokenContext, out string error, out TokenString parsed) { // parse parsed = new TokenString(rawValue, tokenContext); // validate unknown tokens if (parsed.InvalidTokens.Any()) { error = $"found unknown tokens ({string.Join(", ", parsed.InvalidTokens.OrderBy(p => p))})"; parsed = null; return(false); } // validate tokens foreach (TokenName tokenName in parsed.Tokens) { IToken token = tokenContext.GetToken(tokenName, enforceContext: false); if (token == null) { error = $"{{{{{tokenName}}}}} can't be used as a token because that token could not be found."; // should never happen parsed = null; return(false); } if (token.CanHaveMultipleValues(tokenName)) { error = $"{{{{{tokenName}}}}} can't be used as a token because it can have multiple values."; parsed = null; return(false); } } // looks OK error = null; return(true); }
/// <summary>Parse a JSON structure which can contain tokens, and validate that it's valid.</summary> /// <param name="rawJson">The raw JSON structure which may contain tokens.</param> /// <param name="tokenContext">The tokens available for this content pack.</param> /// <param name="migrator">The migrator which validates and migrates content pack data.</param> /// <param name="error">An error phrase indicating why parsing failed (if applicable).</param> /// <param name="parsed">The parsed value, which may be legitimately <c>null</c> even if successful.</param> private bool TryParseJsonTokens(JToken rawJson, IContext tokenContext, IMigration migrator, out string error, out TokenisableJToken parsed) { if (rawJson == null) { error = null; parsed = null; return(true); } // parse parsed = new TokenisableJToken(rawJson, tokenContext); if (!migrator.TryMigrate(parsed, out error)) { return(false); } // validate tokens TokenString[] tokenStrings = parsed.GetTokenStrings().ToArray(); if (tokenStrings.Any()) { // validate unknown tokens string[] unknownTokens = tokenStrings.SelectMany(p => p.InvalidTokens).OrderBy(p => p).ToArray(); if (unknownTokens.Any()) { error = $"found unknown tokens ({string.Join(", ", unknownTokens)})"; parsed = null; return(false); } // validate tokens foreach (TokenName tokenName in tokenStrings.SelectMany(p => p.Tokens)) { IToken token = tokenContext.GetToken(tokenName, enforceContext: false); if (token == null) { error = $"{{{{{tokenName}}}}} can't be used as a token because that token could not be found."; // should never happen parsed = null; return(false); } if (token.CanHaveMultipleValues(tokenName)) { error = $"{{{{{tokenName}}}}} can't be used as a token because it can have multiple values."; parsed = null; return(false); } } } // looks OK if (parsed.Value.Type == JTokenType.Null) { parsed = null; } error = null; return(true); }
/// <summary>Parse a boolean <see cref="PatchConfig.Enabled"/> value from a string which can contain tokens, and validate that it's valid.</summary> /// <param name="rawValue">The raw string which may contain tokens.</param> /// <param name="tokenContext">The tokens available for this content pack.</param> /// <param name="migrator">The migrator which validates and migrates content pack data.</param> /// <param name="error">An error phrase indicating why parsing failed (if applicable).</param> /// <param name="parsed">The parsed value.</param> private bool TryParseEnabled(string rawValue, IContext tokenContext, IMigration migrator, out string error, out bool parsed) { parsed = false; // analyse string if (!this.TryParseStringTokens(rawValue, tokenContext, migrator, out error, out ITokenString tokenString)) { return(false); } // validate & extract tokens string text = rawValue; if (tokenString.HasAnyTokens) { // only one token allowed if (!tokenString.IsSingleTokenOnly) { error = "can't be treated as a true/false value because it contains multiple tokens."; return(false); } // parse token LexTokenToken lexToken = tokenString.GetTokenPlaceholders(recursive: false).Single(); IToken token = tokenContext.GetToken(lexToken.Name, enforceContext: false); ITokenString input = new TokenString(lexToken.InputArg, tokenContext); // check token options InvariantHashSet allowedValues = token?.GetAllowedValues(input); if (token == null || token.IsMutable || !token.IsReady) { error = $"can only use static tokens in this field, consider using a {nameof(PatchConfig.When)} condition instead."; return(false); } if (allowedValues == null || !allowedValues.All(p => bool.TryParse(p, out _))) { error = "that token isn't restricted to 'true' or 'false'."; return(false); } if (token.CanHaveMultipleValues(input)) { error = "can't be treated as a true/false value because that token can have multiple values."; return(false); } text = token.GetValues(input).First(); } // parse text if (!bool.TryParse(text, out parsed)) { error = $"can't parse {tokenString.Raw} as a true/false value."; return(false); } return(true); }
/// <summary>Parse a boolean <see cref="PatchConfig.Enabled"/> value from a string which can contain tokens, and validate that it's valid.</summary> /// <param name="rawValue">The raw string which may contain tokens.</param> /// <param name="tokenContext">The tokens available for this content pack.</param> /// <param name="error">An error phrase indicating why parsing failed (if applicable).</param> /// <param name="parsed">The parsed value.</param> private bool TryParseEnabled(string rawValue, IContext tokenContext, out string error, out bool parsed) { parsed = false; // analyse string if (!this.TryParseTokenString(rawValue, tokenContext, out error, out TokenString tokenString)) { return(false); } // validate & extract tokens string text = rawValue; if (tokenString.HasAnyTokens) { // only one token allowed if (!tokenString.IsSingleTokenOnly) { error = "can't be treated as a true/false value because it contains multiple tokens."; return(false); } // check token options TokenName tokenName = tokenString.Tokens.First(); IToken token = tokenContext.GetToken(tokenName, enforceContext: false); InvariantHashSet allowedValues = token?.GetAllowedValues(tokenName); if (token == null || token.IsMutable || !token.IsValidInContext) { error = $"can only use static tokens in this field, consider using a {nameof(PatchConfig.When)} condition instead."; return(false); } if (allowedValues == null || !allowedValues.All(p => p == true.ToString() || p == false.ToString())) { error = "that token isn't restricted to 'true' or 'false'."; return(false); } if (token.CanHaveMultipleValues(tokenName)) { error = "can't be treated as a true/false value because that token can have multiple values."; return(false); } text = token.GetValues(tokenName).First(); } // parse text if (!bool.TryParse(text, out parsed)) { error = $"can't parse {tokenString.Raw} as a true/false value."; return(false); } return(true); }