Attribute Macro starlark_derive::starlark_module

source ·
#[starlark_module]
Expand description

Write Starlark modules concisely in Rust syntax.

For example:

#[starlark_module]
fn global(builder: &mut GlobalsBuilder) {
    fn cc_binary(name: &str, srcs: Vec<&str>) -> String {
        Ok(format!("{:?} {:?}", name, srcs))
    }
}

Parameters operate as named parameters of a given type. Each parameter will be unpacked with UnpackValue, unless:

  • It is type Option, in which case it will be considered optional.
  • It is a single argument of type Arguments, in which case all arguments will be passed together with minimal interpretation.

There are a number of attributes that can be add to each parameter by writing attributes before the parameter name:

  • #[starlark(default = "a default")] - provide a deafult for the parameter if it is omitted.
  • #[starlark(require = pos)] - require the parameter to be passed by position, not named.
  • #[starlark(require = named)] - require the parameter to be passed by name, not by position.
  • #[starlark(args)] - treat the argument as *args in Starlark, receiving all additional positional arguments as a tuple.
  • #[starlark(kwargs)] - treat the argument as **kwargs in Starlark, receiving all additional named arguments as a dictionary.

There are a number of attributes that can be applied to the entire function by writing attributes before the fn of the function:

  • #[starlark(attribute_type = "foo")] - if the function has .type applied, return this string. Usually used on constructor functions so that ctor.type can be used in Starlark code.
  • #[starlark(return_type = "foo")] - the return type of the function used for documention.
  • #[starlark(speculative_exec_safe)] - the function is considered safe to execute speculatively: the function should have no global side effects, should not panic, and should finish in reasonable time. The evaluator may invoke such functions early to generate more efficient code.
  • #[starlark(attribute)] to turn the name into an attribute on the value. Such a function must take exactly one argument, namely a value of the type you have attached it to.

Multiple attributes can be specified either separately #[starlark(require = named)] #[starlark(default = "")] or separated with a comman #[starlark(require = named, default = "")].

There are two special arguments, distinguished by their type, which provides access to interpreter state:

  • heap: &'v Heap gives access to the Starlark heap, for allocating things.
  • eval: &mut Evaluator<'v, '_> gives access to the Starlark evaluator, which can be used to look at interpreter state.

A module can be used to define globals (with GlobalsBuilder) or methods on an object (with MethodsBuilder). In the case of methods, the first argument to each function will be the object itself, typically named this.

All these functions interoperate properly with dir(), getattr() and hasattr().

If a desired function name is also a Rust keyword, use the r# prefix, e.g. r#type.

As a more complex example:

#[starlark_module]
fn methods(builder: &mut MethodsBuilder) {
    fn r#enum<'v>(
        this: Value<'v>,
        #[starlark(require = named, default = 3)] index: i32,
        heap: &'v Heap,
    ) -> anyhow::Result<StringValue<'v>> {
        Ok(heap.alloc_str(&format!("{this} {index}")))
    }
}

This defines a method such that when attached to an object object.enum(index = 12) will return the string of the object and the index.