Make the naming scheme fully user-editable, with the user's preferred format as default

- ExpressionFormat::compile_scriptlet now falls back to treating a {token} as a
  literal multi-word binding key when it's neither a valid Rhai identifier nor
  valid Rhai script, but still looks like a plain human-typed field name (only
  letters/digits/underscores/spaces). This is what lets `{show name}` work
  without anyone needing to know Rhai, while real Rhai script (even containing
  spaces) still takes priority and genuine syntax errors still surface.
- New default naming scheme app-wide: `{show name} - {season number}x
  {episode number} - {episode name}` (previously the terser {n} - {s00e00} -
  {t} form). Match/Fetch Data auto-upgrade targets updated to match, and
  sample items carry both the short FileBot-style keys and the new
  human-readable ones side by side.
- Naming scheme editor UI rewritten: explains the field is fully editable,
  lists available fields grouped by when they become available (always /
  after Match / after Fetch Data), and clarifies Rhai script is also
  supported for anyone who wants it.
- 3 new unit tests for the plain-field-name fallback (multi-word lookup
  works, real Rhai still takes priority, genuine syntax errors still error).
This commit is contained in:
Renamr Scaffold
2026-08-29 03:15:21 +00:00
parent d1fc4039a7
commit f89589bc88
7 changed files with 240 additions and 22 deletions
+36 -7
View File
@@ -109,13 +109,42 @@ fn compile_scriptlet(token: &str, expression: &str) -> Result<Segment, Expressio
return Ok(Segment::Variable(token.to_string()));
}
engine::shared()
.compile(token)
.map(Segment::Script)
.map_err(|e| ExpressionError::Syntax {
expression: expression.to_string(),
message: e.to_string(),
})
match engine::shared().compile(token) {
Ok(ast) => Ok(Segment::Script(ast)),
Err(e) => {
// Not valid as a single bare identifier, and not valid as a Rhai script
// either - but if it otherwise looks like a plain, human-typed field name
// (e.g. "show name": just letters/digits/spaces/underscores), treat the
// whole thing as a literal binding key instead of surfacing a syntax
// error. This is what lets a naming scheme like
// `{show name} - {season number}x{episode number} - {episode name}` work
// without every such phrase needing to be a reserved single-word binding.
// Genuine script syntax errors (unmatched parens/quotes, bad operators,
// etc.) still get reported exactly as before.
let trimmed = token.trim();
if is_plain_field_name(trimmed) {
Ok(Segment::Variable(trimmed.to_string()))
} else {
Err(ExpressionError::Syntax {
expression: expression.to_string(),
message: e.to_string(),
})
}
}
}
}
/// True for a token that reads like a plain (possibly multi-word) field name rather
/// than code: starts with a letter or underscore, and contains only letters, digits,
/// underscores, and internal spaces. Only consulted as a fallback after Rhai has
/// already rejected the token as a script.
fn is_plain_field_name(token: &str) -> bool {
let mut chars = token.chars();
match chars.next() {
Some(c) if c.is_alphabetic() || c == '_' => {}
_ => return false,
}
chars.all(|c| c.is_alphanumeric() || c == '_' || c == ' ')
}
/// Splits an expression into literal text and `{ }` scriptlets, honoring nested braces