Mixins for Rust

Build Status

Write code in arbitrary languages, to emit Rust code right in your crate.

```rust

![feature(plugin)]

![plugin(external_mixin)]

![plugin(rust_mixin)]

pythonmixin! {" x = 1 + 2 print('fn getx() -> u64 { %d }' % x) "}

fn main() { let value = get_x();

let other_value = rust_mixin! {r#"

fn main() { println!("{}", 3 + 4); } "#};

assert_eq!(value, 3);
assert_eq!(other_value, 7);

} ```

This comes in three libraries:

Should I actually use these?

Probably not, this is me experimenting with more language plugins. A more portable/usable way to do this sort of code-generation is via a Cargo build script plus the include! macro.

Some downsides (not exhaustive):

Installation

Both plugin crates are available on crates.io: rust_mixin, external_mixin. Hence, you can add any subset of

toml [dependencies] rust_mixin = "*" external_mixin = "*"

to your Cargo.toml.

rust_mixin

Write Rust to generate your Rust, right in your Rust (yo dawg). The plugin compiles and runs its argument as a Rust program, and then inserts the output into the main crate, similar to a macro_rules! macro.

The rust_mixin plugin takes a single string, containing a Rust program to be compiled with rustc. This program should print valid Rust to stdout. Each rust_mixin invocation is independent of all others. The string argument is macro-expanded before being used, so constructing an invocation with concat!() is legitimate.

The macro supports an optional { ... } block before the string literal, to specify options. The only option supported currently is arg: it can be specified multiple times, and the arguments are passed to rustc in the order given.

Examples

Compute Fibonacci numbers in the best way possible, by making Rust print a function to compute each number:

```rust

![feature(plugin)]

![plugin(rust_mixin)]

rustmixin! {r#" fn main() { println!("fn fib0() -> i32 {{ 0 }}"); println!("fn fib_1() -> i32 {{ 1 }}");

for i in 2..(40 + 1) {
    println!("fn fib_{}() -> i32 {{ fib_{}() + fib_{}() }}",
             i, i - 1, i - 2);
}

} "#}

fn main() { println!("the 30th fibonacci number is {}", fib_30()); } ```

Do the Fibonacci computation at compile time, naively, so we want some optimisations:

```rust

![feature(plugin)]

![plugin(rust_mixin)]

fn main() { let fib30 = rustmixin! { { arg = "-C", arg = "opt-level=3" } r#" fn fib(n: u64) -> u64 { if n <= 1 { n } else { fib(n - 1) + fib(n - 2) } } fn main() { println!("{}", fib(30)) } "#};

println!("the 30th fibonacci number is {}", fib_30);

} ```

external_mixin

Use a variety of scripting languages to generate Rust code. This has an external_mixin! macro that supports arbitrary interpreters, as well as specialise support for several languages: python_mixin!, ruby_mixin!, sh_mixin!, perl_mixin!.

As with rust_mixin! these macros take their program as a string that gets macro expanded, and each invocation is independent of all others. The program should print valid Rust to stdout. Options can be specified with an optional { ... } block, before the string literal.

The external_mixin! macro is the most flexible form, it takes a compulsory interpreter argument: this program is called with a file containing the code snippet as the last argument.

Both external_mixin! and the language specific macros support the arg option, which can be specified multiple times and are passed to the main binary, in the order given.

Portability?

These macros rely on shelling out to interpreters, relying on there being an appropriately named executable in the user's path (hopefully it is the right version, too...). Hence, this is not portable or reliable. At least a user of rust_mixin! is guarantee to have a rustc available, no such guarantee exists here.

Examples

Count how many files/folders lie at the top of the (Unix) file system.

```rust

![feature(plugin)]

![plugin(external_mixin)]

fn main() { let filecount = shmixin!("ls / | wc -l"); println!("there are {} files in /", file_count); } ```

Compute the Unix time that the program was built at, via Ruby.

```rust

![feature(plugin)]

![plugin(external_mixin)]

fn main() { let buildtime = rubymixin!("puts Time.now.to_i"); } ```

Use Python 2's naked print statement and Python 3's division semantics (and guess the version of the python binary, used by python_mixin!):

```rust

![feature(plugin)]

![plugin(external_mixin)]

fn main() { let value2 = externalmixin! { { interpreter = "python2" } "print 1 / 2" }; let value3 = externalmixin! { { interpreter = "python3" } "print(1 / 2)" }; let valueunknown = pythonmixin!("print(1 / 2)");

if value_unknown as f64 == value3 {
    println!("`python_mixin!` is Python 3");
} else {
    println!("`python_mixin!` is Python 2");
}

} ```

external_mixin_umbrella

The top level item of this repository is a library designed to maximise the sharing of code between external_mixin and rust_mixin, so that their implementations are only 100 and 50 lines respectively.