Skip to content

Commit 26e22fc

Browse files
committed
special case jinja expressions and keep string type
1 parent c19839d commit 26e22fc

File tree

4 files changed

+62
-2
lines changed

4 files changed

+62
-2
lines changed

src/recipe/custom_yaml.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,17 @@ impl Render<Node> for ScalarNode {
161161
label = jinja_error_to_label(&err)
162162
)]
163163
})?;
164+
println!("Rendered: {}", rendered);
165+
if rendered.starts_with("\"") && rendered.ends_with("\"") {
166+
// remove quotes and don't coerce string
167+
let rendered = rendered[1..rendered.len() - 1].to_string();
168+
println!("Rendered without quotes: {}", rendered);
169+
return Ok(Node::from(ScalarNode::new(
170+
*self.span(),
171+
rendered,
172+
false,
173+
)));
174+
}
164175

165176
Ok(Node::from(ScalarNode::new(
166177
*self.span(),
@@ -184,6 +195,17 @@ impl Render<Option<ScalarNode>> for ScalarNode {
184195
)]
185196
})?;
186197

198+
// if rendered string starts with `"` and ends with `"` then remove them and turn may_coerce to false
199+
if rendered.starts_with("\"") && rendered.ends_with("\"") {
200+
// remove quotes and don't coerce string
201+
let rendered = rendered[1..rendered.len() - 1].to_string();
202+
return Ok(Some(ScalarNode::new(
203+
*self.span(),
204+
rendered,
205+
false,
206+
)));
207+
}
208+
187209
if rendered.is_empty() {
188210
Ok(None)
189211
} else {
@@ -228,6 +250,7 @@ impl Render<Node> for SequenceNode {
228250

229251
impl Render<Node> for SequenceNodeInternal {
230252
fn render(&self, jinja: &Jinja, name: &str) -> Result<Node, Vec<PartialParsingError>> {
253+
println!("Rendering: {:?}", self);
231254
match self {
232255
Self::Simple(n) => n.render(jinja, name),
233256
Self::Conditional(if_sel) => {

src/recipe/custom_yaml/rendered.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,18 @@ impl Render<RenderedNode> for ScalarNode {
669669
label = jinja_error_to_label(&err),
670670
)]
671671
})?;
672+
673+
if rendered.starts_with("\"") && rendered.ends_with("\"") {
674+
// remove quotes
675+
let rendered = rendered[1..rendered.len() - 1].to_string();
676+
return Ok(RenderedNode::Scalar(RenderedScalarNode::new(
677+
*self.span(),
678+
self.as_str().to_string(),
679+
rendered,
680+
false,
681+
)));
682+
}
683+
672684
// unsure whether this should be allowed to coerce // check if it's quoted?
673685
let rendered = RenderedScalarNode::new(
674686
*self.span(),

src/recipe/jinja.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ pub struct Jinja {
5454
context: BTreeMap<String, Value>,
5555
}
5656

57+
struct ForceString {
58+
value: String,
59+
force: bool,
60+
}
61+
5762
impl Jinja {
5863
/// Create a new Jinja instance with the given selector configuration.
5964
pub fn new(config: SelectorConfig) -> Self {
@@ -96,6 +101,22 @@ impl Jinja {
96101

97102
/// Render a template with the current context.
98103
pub fn render_str(&self, template: &str) -> Result<String, minijinja::Error> {
104+
if template.starts_with("${{") && template.ends_with("}}") {
105+
// render as expression so that we know the type of the result, and can stringify accordingly
106+
// If we find something like "${{ foo }}" then we want to evaluate it type-safely and make sure that the MiniJinja type is kept
107+
println!("eval: {:?}", template);
108+
let tmplt = &template[3..template.len() - 2];
109+
let expr = self.env.compile_expression(tmplt)?;
110+
let evaled = expr.eval(self.context())?;
111+
if let Some(s) = evaled.to_str() {
112+
// quote the string so that YAML is not getting confused
113+
println!("evaled: {:?}", s);
114+
return Ok(format!("\"{}\"", s));
115+
} else {
116+
return Ok(evaled.to_string());
117+
}
118+
}
119+
99120
self.env.render_str(template, &self.context)
100121
}
101122

@@ -741,7 +762,7 @@ impl Env {
741762
match std::env::var(env_var) {
742763
Ok(r) => Ok(Value::from(r)),
743764
Err(_) => match default_value {
744-
Some(default_value) => Ok(Value::from(default_value)),
765+
Some(default_value) => Ok(Value::from_safe_string(default_value)),
745766
None => Err(minijinja::Error::new(
746767
minijinja::ErrorKind::InvalidOperation,
747768
format!("Environment variable {env_var} not found"),

src/recipe/parser.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,9 @@ impl Recipe {
182182

183183
// add context values
184184
let mut context: IndexMap<String, Variable> = IndexMap::new();
185-
186185
if let Some(context_map) = root_node.get("context") {
186+
println!("Rendering context: {:#?}", context);
187+
187188
let context_map = context_map.as_mapping().ok_or_else(|| {
188189
vec![_partialerror!(
189190
*context_map.span(),
@@ -234,6 +235,9 @@ impl Recipe {
234235
continue;
235236
};
236237
context.insert(k.as_str().to_string(), variable.clone());
238+
239+
println!("Context variable with type: k {} {:#?}", k.as_str(), variable);
240+
237241
// also immediately insert into jinja context so that the value can be used
238242
// in later jinja expressions
239243
jinja

0 commit comments

Comments
 (0)