Skip to content

Instantly share code, notes, and snippets.

@Eastwooder
Created January 6, 2025 21:49
Show Gist options
  • Select an option

  • Save Eastwooder/100df7445b491fec09668d015c6ea0f1 to your computer and use it in GitHub Desktop.

Select an option

Save Eastwooder/100df7445b491fec09668d015c6ea0f1 to your computer and use it in GitHub Desktop.
Using Starlark as plugin language for rust
[package]
name = "starlark-playground"
version = "0.1.0"
edition = "2021"
[dependencies]
allocative = "0.3.4"
indoc = "2.0.5"
starlark = "0.13.0"
use allocative::Allocative;
use indoc::indoc;
use starlark::any::ProvidesStaticType;
use starlark::environment::{GlobalsBuilder, Module};
use starlark::eval::Evaluator;
use starlark::syntax::{AstModule, Dialect};
use starlark::values::none::NoneType;
use starlark::values::{starlark_value, Heap, NoSerialize, StarlarkValue, Value};
use starlark::{starlark_module, starlark_simple_value};
use std::cell::RefCell;
use std::fmt::{Display, Formatter};
fn main() -> starlark::Result<()> {
let content = indoc! {"
def my_loop(members):
for item in members:
emit(item)
emit(fetch().name)
my_loop(fetch().members)
"};
let ast = AstModule::parse("complex.star", content.to_owned(), &Dialect::Standard)?;
let globals = GlobalsBuilder::new().with(starlark_fetch).build();
let module = Module::new();
let store = Store::default();
{
let mut eval = Evaluator::new(&module);
eval.extra = Some(&store);
let _ = eval.eval_module(ast, &globals)?;
}
for emitted in &*store.0.borrow() {
println!("emitted: {emitted}");
}
Ok(())
}
// Define a store in which to accumulate JSON strings
#[derive(Debug, ProvidesStaticType, Default)]
struct Store(RefCell<Vec<String>>);
impl Store {
fn add(&self, x: String) {
self.0.borrow_mut().push(x)
}
}
#[starlark_module]
fn starlark_fetch(builder: &mut GlobalsBuilder) {
fn fetch(eval: &mut Evaluator) -> starlark::Result<Extr> {
Ok(Extr {
name: "test".to_string(),
members: vec!["a".to_string(), "b".to_string()],
})
}
fn emit(x: Value, eval: &mut Evaluator) -> starlark::Result<NoneType> {
// We modify extra (which we know is a Store) and add the JSON of the
// value the user gave.
eval.extra
.unwrap()
.downcast_ref::<Store>()
.unwrap()
.add(x.to_json()?);
Ok(NoneType)
}
}
#[derive(Debug, Clone, ProvidesStaticType, NoSerialize, Allocative)]
pub struct Extr {
pub name: String,
pub members: Vec<String>,
}
starlark_simple_value!(Extr);
impl Display for Extr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Extr[name={name}]", name = self.name)
}
}
#[starlark_value(type = "Extr")]
impl<'v> StarlarkValue<'v> for Extr {
fn get_attr(&self, attribute: &str, heap: &'v Heap) -> Option<Value<'v>> {
match attribute {
"name" => Some(heap.alloc(self.name.clone())),
"members" => Some(heap.alloc(self.members.clone())),
_ => None,
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment