[Question] Creating a function using the `Dict` type as an argument.
hulto opened this issue · comments
Sorry if this is the wrong place but I'm having issues using the rust Dict type.
I'm creating a template
function to expand the starlark stdlibrary.
The function will take a JINJA2 template and using Tera format it with arbitrary variables.
I'm currently using a serede_json object to pass the template vars but I would like to switch to the Starlark Dict type so the values can be more easily worked with.
file.rs
mod template_impl;
use starlark::environment::{Methods, MethodsBuilder, MethodsStatic};
use starlark::values::{StarlarkValue, Value, UnpackValue, ValueLike, dict::Dict};
use starlark::values::none::NoneType;
use starlark::{starlark_type, starlark_simple_value, starlark_module};
...
#[starlark_module]
fn methods(builder: &mut MethodsBuilder) {
...
fn template(_this: FileLibrary, template_path: String, dst_path: String, args: Dict, autoescape: bool) -> NoneType {
template_impl::template(template_path, dst_path, args, autoescape)?;
Ok(NoneType{})
}
fn write(_this: FileLibrary, path: String, content: String) -> NoneType {
write_impl::write(path, content)?;
Ok(NoneType{})
}
}
This gives the following error.
I came across these two issues:
- #14 - My understanding is this is a good place to look if I wanted to create my own types but I'd like to use the Dict type.
- #22 - I see how I could unpack & create a Dict from a rust SmallMap but i'm still not sure how to define a rust function to take a Starlark Dict type.
What rust type should I specify in my function definition if I want to create a function that accepts a Starlark Dict?
If performance is not critical, try SmallMap<String, Value>
instead of Dict
.
You can also use DictOf<...>
.
Thanks you so much for the quick response @stepancheg :)
That did the trick! I have a follow up question:
I'm currently iterating over each key in the SmallMap and inserting the value into the tera::Context
with serde_json I'm able to serialize the Value
type.
fn build_context_json(json_data: String) -> Result<Context> {
let serde_dict: serde_json::Value = serde_json::from_str(json_data.as_str())?;
let mut context = Context::new();
for (key, value) in serde_dict.as_object().unwrap() {
context.insert(key.as_str(), &value);
}
return Ok(context);
}
It looks like the starlark Value
type doesn't have a serialize
/ deserialize
function.
My first thought is to iterate through and extract each type String, Int, etc. and pass those in using the get_type()
function and <Type>::unpack_value()
functions.
fn build_context_smallmap(dict_data: SmallMap<String, Value>) -> Result<Context> {
let mut context = Context::new();
for (key, value) in dict_data {
println!("{}", value.get_type() );
if value.get_type() == "string" {
let tmp = String::unpack_value(value);
context.insert(key.as_str(), &tmp);
} else if value.get_type() == "int" {
let tmp = i32::unpack_value(value);
context.insert(key.as_str(), &tmp);
} else if value.get_type() == "bool" {
let tmp = bool::unpack_value(value);
context.insert(key.as_str(), &tmp);
}
}
return Ok(context);
}
#[cfg(test)]
mod tests {
#[test]
fn test_template_build_context() -> anyhow::Result<()>{
let map: SmallMap<String, Value> = smallmap! {
"name".to_string() => Value::new_frozen(const_frozen_string!("greg").unpack()),
"age".to_string() => Value::new_int(29),
"admin".to_string() => Value::new_bool(true),
};
let res = build_context(map)?;
println!("{:?}", res);
Ok(())
}
}
Is there a better way to extract a serializable object from the Value
type?
Is there a way to create a Value
from a SmallMap
or List
/Vec
to create a test for nested JSON types?
Eg.
{
"jobs": [
{
"name":"test",
"jobid":1
},
{
"name":"test2",
"jobid":2
},
{
"name":"job3",
"jobid":3
}
]
}
There is Serialize for Value.
Ah thank you looks like were using too old of a version 0.6.0.
I'll try updating 🙂
Thank you!