Skip to main content

Input

Struct Input 

Source
pub struct Input {
    pub format: Option<String>,
    pub options: Option<TokenStream>,
    pub list: ContList,
    pub entries: Vec<Entry>,
    pub rest: TokenStream,
}
Expand description

What a callback receives: @announce_format "1" (OPTIONS)? [LIST] ENTRY* REST.

§The wire format

A callback receives these tokens:

@announce_format "1" (OPTIONS)? [LIST] ENTRY* REST
  • @announce_format "1": the format prefix. Every call that announce makes to the next element of a chain starts with it, whatever the element is: a trampoline, a transformer, a callback, a check. "1" is the version of the wire format (ANNOUNCE_FORMAT); it changes when this format changes in a way an old reader cannot read. A call written by hand does not need the prefix, and every parser of announce reads a call with or without it (Input::format is the version found). A call with another version is one compile error that names both versions, because the item and the macro that reads it were built with incompatible versions of announce. How a macro of your own detects the prefix. It starts every call that announce makes, also the call of the sentinel of the runner, NAME! { @announce_format "1" @__announce_exists }. (The runner is the hidden attribute that #[announce] puts on an item that has other attributes. Its arguments have their own marker, __announce_format_version = "1": it versions the encoding of the arguments of an attribute, which the runners of two versions of announce compare when they meet on one item. The runner is not a call of a chain, and has no @announce_format; the calls of a chain are not attributes, and have no __announce_format_version.)
  • OPTIONS: global options, forwarded unchanged by every hop. Only a leading expr has a meaning: it puts the chain in expression position, where derive and attribute elements are refused (is_expression_position). The rest is free for helpers (Input::has_option).
  • LIST: the continuation list, the calls still to make, head first. The head is the macro that is called. An element (Cont) is path!, #[derive(path)] Name or #[attr] Name. The last two are the derive and attribute route: Name is a dummy enum that carries the tokens, and a derive or attribute macro hands them on with redirect.
  • ENTRY: KEY => [META] { ITEM }, one per definition (Entry). KEY is the list element that produced the entry, as written.
  • REST: tokens that the protocol never touches. A parser reads entries greedily, so wrap a REST that starts like an entry.

How a chain runs:

  • Each macro drops its own element (the head) from the list, adds an entry, and calls the next element with the rest of the list. The last element is the callback: it runs once, with one entry per trampoline.
  • New entries go in front, so they are newest first. Input::entries_in_order gives oldest first.
  • [META] is always written, even when empty ([]).
  • A trampoline can get the prefixes @__announce_self { PATH } and @__announce_dollar { $ } after the format prefix, before the rest of this input: @announce_format "1" @__announce_self { PATH } @__announce_dollar { $ } ... Only trampolines and redirect use them. Parsing an Input skips them.
  • The calls that announce makes to a check have the prefix too: the query @announce_format "1" @__announce_attrs [..] { STATE } and the check itself, @announce_format "1" (ARGS), ITEM (attrs).

META holds these elements, as the definer wrote them:

  • path or path(args): a check (#[announce(path(args))]);
  • path(args): a meta = path(args) entry, never called (Entry::find_meta);
  • mock(path): the item stands in for the item at path (Entry::mock);
  • attrs(a, b): the attribute names that checks own (Entry::owned_attrs);
  • type_leak(..): the type table of a repeater (Entry::type_leak).

Ignore elements you do not know.

use announce::helper::Input;

let input: Input = syn::parse_quote! {
    @announce_format "1"
    [cb!]
    a::F! => [my_lib::check, mock(::core::iter::Iterator)] { pub trait F {} }
    REST tokens
};
assert_eq!(input.format.as_deref(), Some("1"));
let entry = &input.entries[0];
assert!(entry.has_check("check"));
assert!(entry.mock().is_some());
assert_eq!(input.rest.to_string(), "REST tokens");

// A call by hand has no prefix, and parses as well.
let by_hand: Input = syn::parse_quote! { [cb!] a::F! => [] { pub trait F {} } };
assert_eq!(by_hand.format, None);

// Another version is one error, which names the version found and the one read.
let err = syn::parse2::<Input>(syn::parse_quote! { @announce_format "2" [cb!] }).unwrap_err();
assert!(err.to_string().contains("found `@announce_format \"2\"`"));
assert!(err.to_string().contains("expected `@announce_format \"1\"`"));

§Detecting a call from announce

A macro of your own, a macro_rules! callback, check or transformer, can match the prefix to know that announce called it, and which version of the wire format the call has. Put the arm for the prefix first. The arm for the call by hand (without it) stays, if you want one:

macro_rules! who_calls {
    // Called by announce: the prefix, then `[LIST] ENTRY* REST`.
    (@announce_format "1" [$($list:tt)*] $($rest:tt)*) => {
        const CALLER: &str = "announce";
    };
    // Called by hand, or with a version that this macro does not know.
    ($($rest:tt)*) => {
        const CALLER: &str = "not announce, or another format";
    };
}

who_calls! { @announce_format "1" [who_calls!] }
assert_eq!(CALLER, "announce");

A macro that forwards the call to the next element starts the forwarded call with the prefix again; Input::forward does it, and format_prefix gives the tokens to a proc macro that builds the call itself. A macro_rules! callback that is written for one version only says so in its pattern, as above: a call with another version matches no arm, and rustc reports the call.

§A real example

This is what a callback gets from a real chain, captured by the test tests/user/tests/wire.rs. The definitions, in tests/defs/src/wire.rs (Meters is private to the defining crate, so Length has a repeater):

ⓘ
::announce::define_repeater!(pub Repeater);

/// A point.
#[announce(announce_test_derive::lib_check, meta = crate::info(1))]
pub struct Point {
    /// The x coordinate.
    #[my_lib(skip)]
    pub x: u8,
    pub y: u8,
}

#[announce(repeater = crate::wire::Repeater)]
pub struct Length(pub Meters);

The chain, in another crate. show! is the callback; it prints its whole input:

ⓘ
use announce_test_defs::wire as defs;

macro_rules! show {
    ($($t:tt)*) => { stringify!($($t)*) };
}

let wire: &str = defs::Point! { (expr) [defs::Point!, defs::Length!, show!] some rest };

wire is this text. The tokens are the ones received, in order. Only the layout is added by hand, and the random number of id (a different one for each build) is replaced by ID. The test compares this block with what the chain gives, so it cannot drift:

@announce_format "1"
(expr)
[show!]
defs::Length! => [
    type_leak(repeater = $crate::wire::Repeater, id = ID,
              context = 0, referrer = ["type-leak-format-14" (Meters)])
] {
    pub struct Length(pub Meters);
}
defs::Point! => [
    announce_test_derive::lib_check,
    $crate::info(1),
    attrs(my_lib)
] {
    pub struct Point {
        #[my_lib(skip)]
        pub x: u8,
        pub y: u8,
    }
}
some rest

How the parts map to the fields of Input:

TokensFieldNotes
@announce_format "1"formatThe prefix, written by both hops.
(expr)optionsFrom the call, forwarded by both hops.
[show!]listOnly the callback is left: Point! and Length! each dropped themselves.
defs::Length! => [..] { .. }entries[0]The newest entry, the last hop. Its key is as written in the call.
defs::Point! => [..] { .. }entries[1]The oldest entry.
some restrestThe caller’s tokens, untouched.

What is in the entries:

  • Length: type_leak(..) is the table of the repeater (Entry::type_leak). The item still says Meters, which only the defining crate can resolve; the table is what a callback uses to write the type from anywhere (TypeLeak::apply).
  • Point: announce_test_derive::lib_check is the check, $crate::info(1) is meta = crate::info(1) (crate became $crate, so that the path resolves where the callback runs), and attrs(my_lib) lists the attribute names that the check owns.
  • The doc comments of Point are gone from the item, because the payload has no docs unless docs = true. #[my_lib(skip)] is still there: the definer removed it from the item it emitted, but the callback can read it.

§self and hygiene

self is hygienic. The self in the receiver of a method that the callback receives (&self) belongs to the trampoline’s expansion; a self that the callback writes itself, in its own quote!, is another one to rustc. A callback that mixes the received tokens with its own output, for example the received signature with a body it writes, must use the self token of the payload in that body. Otherwise rustc reports E0424 (“cannot find value self in this scope”). Take the token from the receiver of the parsed signature:

use announce::helper::Input;
use template_quote::quote;

// For `trait Get { fn get(&self) -> u8; }`: an impl that forwards `get` to a field.
fn forward(input: &Input) -> syn::Result<proc_macro2::TokenStream> {
    let item = match input.entries[0].item()? {
        syn::Item::Trait(t) => t,
        _ => return Err(syn::Error::new(proc_macro2::Span::call_site(), "expected a trait")),
    };
    let sig = match &item.items[0] {
        syn::TraitItem::Fn(f) => &f.sig,
        _ => return Err(syn::Error::new(proc_macro2::Span::call_site(), "expected a method")),
    };
    let (trait_name, name) = (&item.ident, &sig.ident);
    // The `self` of the payload: a `quote! { self.inner }` here would not match `#sig`.
    let this = &sig.receiver().expect("a method with a receiver").self_token;
    Ok(quote! {
        impl #trait_name for Wrapper { #sig { #this.inner.#name() } }
    })
}

let input: Input = syn::parse_quote! { [cb!] a::Get! => [] { trait Get { fn get(&self) -> u8; } } };
let out = forward(&input).unwrap().to_string();
assert!(out.contains("fn get (& self) -> u8 { self . inner . get () }"), "{}", out);

Fields§

§format: Option<String>

The version of the prefix @announce_format "VERSION" that the call started with (ANNOUNCE_FORMAT when there is one), or None for a call without it (written by hand). A call with another version does not parse.

forward, fetch and gather always write the prefix with the version of this crate, whatever this field is.

§options: Option<TokenStream>

The global options group (..). forward passes it on unchanged. fetch and gather add __announce_fetched(..) to it, which records what they fetched (to detect a continuation that is not a trampoline).

§list: ContList

The continuation list; its head is the callback itself.

§entries: Vec<Entry>

Newest first.

§rest: TokenStream

REST: tokens after the entries, untouched by the protocol.

Implementations§

Source§

impl Input

Source

pub fn newest(&self) -> Option<&Entry>

The newest entry, the one a transformer rewrites.

Source

pub fn forward_transformed(&self, entry: Entry) -> Result<TokenStream>

For a transformer: forwards to the next element with the newest entry replaced by entry. Errors if there is no entry or no next element.

Source§

impl Input

Source

pub fn entries_in_order(&self) -> impl Iterator<Item = &Entry>

Entries in the order their definitions were visited (oldest first).

Source

pub fn fetch(&self, new: impl IntoIterator<Item = Cont>) -> TokenStream

Fetches more definitions, then calls this callback again. It invokes new[0] with the list [new.., <this list>], so the new entries arrive in front of the old ones.

The paths are recorded in the options group as __announce_fetched(..). If this callback is called again with a recorded path but no entry for it, that path is not an #[announce] trampoline. Then fetch emits a compile_error! instead of fetching until the recursion limit. A callback that fetches only what is still missing (as gather does) never meets that error for a trampoline.

Source

pub fn has_option(&self, name: &str) -> bool

Whether the global options group contains the ident name.

Source

pub fn option_items(&self) -> Vec<TokenStream>

The comma-separated elements of the options group.

Source

pub fn rest_as<T: Parse>(&self) -> Result<T>

Parses REST as T.

Source

pub fn entry_for(&self, cont: &Cont) -> Option<&Entry>

The entry whose key names cont (compared by macro path, with generic arguments ignored), newest first.

Source

pub fn gather(&self, needed: impl IntoIterator<Item = Cont>) -> Gather<'_>

Asks for definitions: “give me these, then call me again”.

Returns Gather::Ready with one entry per requested continuation, in order, when all are present. Otherwise it returns Gather::Fetch: tokens that fetch the missing ones and call this callback again.

ⓘ
let entries = match input.gather(needed) {
    Gather::Ready(e) => e,
    Gather::Fetch(ts) => return Ok(ts),
};
Source

pub fn forward(&self) -> Option<TokenStream>

Continues the chain: drops the head and invokes the next element. Returns None if the head was the last element.

Trait Implementations§

Source§

impl Clone for Input

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Input

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Parse for Input

Source§

fn parse(input: ParseStream<'_>) -> Result<Self>

Auto Trait Implementations§

§

impl !Send for Input

§

impl !Sync for Input

§

impl Freeze for Input

§

impl RefUnwindSafe for Input

§

impl Unpin for Input

§

impl UnsafeUnpin for Input

§

impl UnwindSafe for Input

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.