Polyquine: Teach Rust types to codegen their own constructor!
crates.io: polyquine
cargo add polyquine
This crate contains:
- A
Quinetrait. Types that implementQuinehave a methodctor_tokens(&self) -> TokenStream; The tokens returned by it are valid Rust expression that, when evaluated, produces the original value. - Implementations for:
- All primitives (
i32,bool, etc.) String- Fixed-size arrays
[T; N] - Some
std::collectionstypes (Vec,HashMap,HashSet, etc.) - Tuples of up to 12 elements
Box<T>,Option<T>
- All primitives (
- Declarative macros to implement
Quinefor:- Iterables (
derive_iterable). The given iterable must:- Have an
.iter()method that returns an iterator over its elements. - Implement
From<[T; N]>
- Have an
- Iterables (
- A
#[derive(Quine)]macro to derive the trait for enums and structs. All fields thereof must implementQuine.
For example:
#[derive(Quine)]
struct Node {
value: i32,
next: Option<Box<Node>>,
}
let node = Node {
value: 1i32,
next: Some(Box::new(Node {
value: 2i32,
next: None,
})),
};
assert_ts_eq(
&node.ctor_tokens(),
// Evaluate this and you get the value of `node` back!
"e! {Node { value: 1i32, next: Some(Box::new(Node { value: 2i32, next: None })) }},
);You can supply a custom implementation for a specific variant only - handy when this variant contains a foreign type that does not implement Quine, or when you want some custom logic in its .ctor_tokens().
This is done using the attribute #[polyquine_with(arm = (val) => {...})].
#[derive(Quine)]
enum TestEnum {
A,
#[polyquine_with(arm = (val) => {
let new_val = val + 1;
quote! { TestEnum::B(#new_val) }
})]
B(i32),
}
let b = TestEnum::B(42i32);
assert_ts_eq(&b.ctor_tokens(), "e! { TestEnum::B(43i32) }); // <- the value got incremented!You can also skip variants using #[polyquine_skip].
By doing so, you pinky promise to never call .ctor_tokens() on an instance of that variant.
If you do, it will panic!():
#[derive(Quine)]
enum TestEnum {
A,
#[polyquine_skip]
B,
}
let a = TestEnum::A;
assert_ts_eq(
&a.ctor_tokens(),
"e! {polyquine::quine::test::TestEnum::A},
); // All good for A
let b = TestEnum::B;
let _ = b.ctor_tokens(); // <- This should panicContributions are always welcome!
- If you need
Quineto work with a specific type fromstdor a third-party crate, or have any other feature suggestions, open an issue tagged "feature request". - If you encounter any bugs / unexpected behaviour, open an issue. Please include the error message and relevant code snippets.
- Pull requests are very much appreciated! I will do my best to review them ASAP.
When submitting a patch, please:
- Make sure that the linter is happy and all tests pass:
cargo fmt cargo clippy --workspace --fix cargo test --workspace - Include a short message explaining the changes and reasoning behind them
- Make sure that the linter is happy and all tests pass:
See the issues for planned feature tracking and known bugs
- Version
0.0.2is a complete rewrite. We now use our own trait instead of derivingquote::ToTokens. All field attributes from version0.0.1are no longer supported. - We ignore references, i.e. the implementation for
&Tis the same as forT. This may cause issues - please open an issue if you encounter any. - Please open an issue if any
stdtype is not supported and you need it. Rc,Arcand friends are unlikely to ever be supported. This is due to their shared ownership semantics: We can have multipleRc's pointing to the same memory. When implementingQuine, the best we can do is re-construct their values separately. When evaluated, we will now have multiple independent copies of the original value - not a single value behind a shared reference.
Partially inspired by the parsel crate.
This is intended for cases where you need to construct a value and do some non-trivial logic on it at compile time (probably in a procedural macro), then take the result out of the macro as valid Rust code.
For example, when you are parsing a DSL at compile time and outputting the constructed (and possibly simplified / transformed) AST as the result of your macro call.
See this PR as proof that this use case is not completely made up.
still no.
A quine is a program that outputs its own source code. This crate teaches an arbitrary enum / struct to generate a Rust program that constructs it, thereby making it a quine. So, polyquine! This is only slightly pretentious.
also, trans rights! 🏳️⚧️