Polyquine: Teach Rust types to codegen their own constructor!
crates.io: polyquine
cargo add polyquine
Purpose
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:
let node = Node ;
assert_ts_eq;
Attributes
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) => {...})].
let b = B;
assert_ts_eq; // <- 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!():
let a = A;
assert_ts_eq; // All good for A
let b = B;
let _ = b.ctor_tokens; // <- This should panic
Contributing
Contributions 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
Notes
- 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.
Acknowledgements
Partially inspired by the parsel crate.
Why?
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.
Production readiness
still no.
Why the name?
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! 🏳️⚧️