diff --git a/src-tauri/yaak-templates/src/error.rs b/src-tauri/yaak-templates/src/error.rs index c1df0377..e093f9c2 100644 --- a/src-tauri/yaak-templates/src/error.rs +++ b/src-tauri/yaak-templates/src/error.rs @@ -6,6 +6,9 @@ pub enum Error { #[error("Render Error: {0}")] RenderError(String), + #[error("Render Error: Variable \"{0}\" is not defined in active environment")] + VariableNotFound(String), + #[error("Render Error: Max recursion depth exceeded")] RenderStackExceededError, } diff --git a/src-tauri/yaak-templates/src/parser.rs b/src-tauri/yaak-templates/src/parser.rs index 30470d16..f4d6791c 100644 --- a/src-tauri/yaak-templates/src/parser.rs +++ b/src-tauri/yaak-templates/src/parser.rs @@ -269,9 +269,9 @@ impl Parser { while self.pos < self.chars.len() { let ch = self.peek_char(); let is_valid = if start_pos == self.pos { - ch.is_alphabetic() // First char has to be alphabetic + ch.is_alphabetic() || ch == '_' // First is more restrictive } else { - ch.is_alphanumeric() || ch == '-' || ch == '_' + ch.is_alphanumeric() || ch == '_' || ch == '-' }; if is_valid { text.push(ch); @@ -457,13 +457,13 @@ mod tests { #[test] fn var_prefixes() { - let mut p = Parser::new("${[ -a ]}${[ _a ]}${[ 0a ]}"); + let mut p = Parser::new("${[ -a ]}${[ 0a ]}"); assert_eq!( p.parse().tokens, vec![ Token::Raw { // Shouldn't be parsed, because they're invalid - text: "${[ -a ]}${[ _a ]}${[ 0a ]}".into() + text: "${[ -a ]}${[ 0a ]}".into() }, Token::Eof ] @@ -476,8 +476,8 @@ mod tests { assert_eq!( p.parse().tokens, vec![ - Token::Raw { - text: "${[ _a ]}".into() + Token::Tag { + val: Val::Var { name: "_a".into() } }, Token::Eof ] diff --git a/src-tauri/yaak-templates/src/renderer.rs b/src-tauri/yaak-templates/src/renderer.rs index b87b76d4..a9e378a3 100644 --- a/src-tauri/yaak-templates/src/renderer.rs +++ b/src-tauri/yaak-templates/src/renderer.rs @@ -1,4 +1,4 @@ -use crate::error::Error::RenderStackExceededError; +use crate::error::Error::{RenderStackExceededError, VariableNotFound}; use crate::error::Result; use crate::{FnArg, Parser, Token, Tokens, Val}; use log::warn; @@ -100,7 +100,7 @@ async fn render_tag( let r = Box::pin(parse_and_render_with_depth(v, vars, cb, depth)).await?; r.to_string() } - None => "".into(), + None => return Err(VariableNotFound(name)), }, Val::Bool { value } => value.to_string(), Val::Fn { name, args } => { @@ -142,7 +142,7 @@ async fn render_tag( #[cfg(test)] mod parse_and_render_tests { - use crate::error::Error::{RenderError, RenderStackExceededError}; + use crate::error::Error::{RenderError, RenderStackExceededError, VariableNotFound}; use crate::error::Result; use crate::renderer::TemplateCallback; use crate::*; @@ -200,6 +200,19 @@ mod parse_and_render_tests { Ok(()) } + #[tokio::test] + async fn render_missing_var() -> Result<()> { + let empty_cb = EmptyCB {}; + let template = "${[ foo ]}"; + let vars = HashMap::new(); + + assert_eq!( + parse_and_render(template, &vars, &empty_cb).await, + Err(VariableNotFound("foo".to_string())) + ); + Ok(()) + } + #[tokio::test] async fn render_self_referencing_var() -> Result<()> { let empty_cb = EmptyCB {}; diff --git a/src-web/components/EnvironmentEditDialog.tsx b/src-web/components/EnvironmentEditDialog.tsx index c84a97d8..589e43e2 100644 --- a/src-web/components/EnvironmentEditDialog.tsx +++ b/src-web/components/EnvironmentEditDialog.tsx @@ -160,7 +160,7 @@ const EnvironmentEditor = function ({ const validateName = useCallback((name: string) => { // Empty just means the variable doesn't have a name yet, and is unusable if (name === '') return true; - return name.match(/^[a-z][a-z0-9_-]*$/i) != null; + return name.match(/^[a-z_][a-z0-9_-]*$/i) != null; }, []); return (