Expand description

accio

This crate is mostly an experiment, and is not intended for production!

accio retrieves code blocks distributed to multiple sites in the build path, and puts them together in the call site.

It does this at the compile time; therefore no code execution takes place in load-time (ctor) or runtime (any kind of lazy initialization).

It allows for partial implementations of functions, enums, struct fields, match statements and more.

There are still many quirks with it; and they are not likely to be solved in the future. Just to name a few;

  • Macro execution paths are not checked AT ALL,
  • Conditional compilation flags are blatantly disregarded,
  • Incremental compilation might cause some problems with when the macros are re-evaluated,
  • Error messages will point to incorrect places,
  • The accio_emit macro is only matched by name; and name overlaps are not checked for,
  • None of the macros in this crate can be nested,
  • There is no way for an accio_emit statement to dynamically refer to its own path,
  • The source path is simply recursively traversed for .rs files; instead of verifying that any of these files are imported somewhere,
  • There are many possible errors during parsing that are silently omitted,
  • It does not work with documentation examples! (Yes, every example in the docs is no_run.)
  • I simply didn’t test how it would behave with multiple crates.

For most use-cases; I can recommend inventory, linkme, or ctor crates.

For automatically importing all modules in a directory, you can use automod.

Contribution

I’m unlikely to respond to issues in this crate; but if you think you can make this usable in any way; go for it. So PRs are welcome I guess.

Use cases

accio can be used for collecting statements in a function body as follows:

use accio::*;

// some_file.rs
accio_emit! {
    // accio_emit's block must be written
    // as `scope_name { code_block }` pairs.
    // multiple blocks can be listed as follows:
    first_scope {
        val = 1;
    }
    second_scope {
        val += 2;
    }
}

// some_other_file.rs
accio_emit! {
    // the same scope name can have multiple
    // blocks; these are merged in an
    // undetermined order.
    second_scope {
        val += 3;
    }
}

// another_file.rs
let mut val = 0;
assert_eq!(val, 0);

// include the first scope:
// this will evaluate to `val = 1;`
accio!(first_scope);
assert_eq!(val, 1);

// include the second scope:
// this will evaluate to either
// `val += 2; val += 3;` or
// `val += 3; val += 2;`.
accio!(second_scope);
assert_eq!(val, 6);

A more meaningful use case is automatically gathering enum variants. However, due to Rust’s grammar rules, the accio! macro cannot be placed inside the braces of an enum or a struct. The following would not compile:

use accio::*;

enum SomeEnum { 
    accio!(enum_variants)
}

This would yield:

error: expected one of `(`, `,`, `=`, `{`, or `}`, found `!`
 --> src/lib.rs:60:10
  |
5 |     accio!(enum_variants)    
  |          ^ expected one of `(`, `,`, `=`, `{`, or `}`

Instead, we can use the accio_body attribute macro:

use accio::*;

#[accio_body(enum_variants)]
enum SomeEnum {
    // this part MUST be empty!
}

#[accio_body(struct_fields)]
struct SomeStruct {
    /* this part MUST be empty! */
}

#[accio_body(array_elems)]
static SOME_ARRAY: &[i32] = &[];

accio_emit! {
    enum_variants {
        FirstVariant,
    }
    enum_variants {
        SecondVariant(String),
    }
    struct_fields {
        pub name: String,
    }
    array_elems: {
        42,
    }
}
// ...and so on

Note that accio_body implementation places the code into the first empty curly brace ({}) or square bracket ([]) scope. Therefore the following variants will fail:

use accio::*;

#[accio_body(enum_variants)]
enum FailingEnum {
    // there is code within the braces
    AlreadySomeVariant,
}

#[accio_body(struct_fields)]
struct FailingStruct; // no braces!

You can still add comments, they do not cause issues.

See examples/ for more detailed examples.

Macros

Collects the tokens emitted by accio_emit! invocations with the same scope (key) and expands to all the tokens collected. The order may not be preserved. Invocations of this macro cannot be nested.

Specifies that a block of code will be ‘emitted’. Without an accio! or an accio_body! invocation, accio_emit! is no-op. Any tokens passed to accio_emit are consumed.

Attribute Macros

Attribute macro variant of accio, for expanding into item body blocks (e.g. struct fields, enum variants, array elements etc). Replaces the first empty curly brace ({}) or square bracket ([]) encountered.