use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
pub trait Quine {
fn ctor_tokens(&self) -> TokenStream;
}
impl<T: Quine> Quine for &T {
fn ctor_tokens(&self) -> TokenStream {
(**self).ctor_tokens()
}
}
impl<T: Quine> Quine for Box<T> {
fn ctor_tokens(&self) -> TokenStream {
let inner = self.as_ref().ctor_tokens();
quote! {Box::new(#inner)}
}
}
impl<T: Quine> Quine for Option<T> {
fn ctor_tokens(&self) -> TokenStream {
match self {
Some(value) => {
let inner = value.ctor_tokens();
quote! {Some(#inner)}
}
None => quote! {None},
}
}
}
impl<T: Quine, const N: usize> Quine for [T; N] {
fn ctor_tokens(&self) -> TokenStream {
let elements = self.iter().map(|elem| elem.ctor_tokens());
quote! {[#(#elements),*]}
}
}
derive_primitive!(
i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, bool, char, &str
);
derive_trivial!(String);
derive_tuple_all!(A B C D E F G H I J K);
derive_iterable!(Vec<T>);
derive_iterable!(VecDeque<T>);
derive_iterable!(HashSet<T>);
derive_iterable!(HashMap<K, V>);
derive_iterable!(BTreeMap<K, V>);
mod test {
use super::*;
#[allow(unused)]
use crate::Quine;
#[allow(unused)]
fn assert_ts_eq(left: &TokenStream, right: &TokenStream) {
assert_eq!(left.to_string(), right.to_string());
}
#[test]
fn test_vec() {
let vec = Vec::from([1i32, 2i32, 3i32]);
assert_ts_eq(&vec.ctor_tokens(), "e! {Vec::from([1i32, 2i32, 3i32])});
}
#[test]
fn test_tuple() {
let tuple = (1i32, true);
assert_ts_eq(&tuple.ctor_tokens(), "e! {(1i32, true)});
}
#[test]
fn test_hashmap() {
let map = HashMap::from([(1i32, "one")]);
assert_ts_eq(&map.ctor_tokens(), "e! {HashMap::from([(1i32, "one")])});
}
#[test]
fn test_box() {
let boxed = Box::new(String::from("hello, world!"));
assert_ts_eq(
&boxed.ctor_tokens(),
"e! {Box::new(String::from("hello, world!"))},
);
}
#[test]
fn test_slice() {
let slice = [1i32, 2i32, 3i32];
assert_ts_eq(&slice.ctor_tokens(), "e! {[1i32, 2i32, 3i32]});
}
#[test]
fn test_struct() {
#[derive(Quine)]
struct TestStruct {
a: i32,
b: bool,
}
let test_struct = TestStruct { a: 1i32, b: true };
assert_ts_eq(
&test_struct.ctor_tokens(),
"e! {TestStruct { a: 1i32, b: true }},
);
}
#[test]
fn test_tuple_struct() {
#[derive(Quine)]
struct TestTupleStruct(i32, bool);
let test_tuple_struct = TestTupleStruct(1i32, true);
assert_ts_eq(
&test_tuple_struct.ctor_tokens(),
"e! {TestTupleStruct(1i32, true)},
);
}
#[test]
fn test_unit_struct() {
#[derive(Quine)]
struct TestUnitStruct;
let test_unit_struct = TestUnitStruct {};
assert_ts_eq(&test_unit_struct.ctor_tokens(), "e! {TestUnitStruct {}});
}
#[test]
fn test_nested_struct() {
#[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(),
"e! {Node { value: 1i32, next: Some(Box::new(Node { value: 2i32, next: None })) }},
);
}
#[test]
fn test_enum() {
#[derive(Quine)]
enum TestEnum {
A,
B(i32),
C { name: String },
}
let a = TestEnum::A;
let b = TestEnum::B(1i32);
let c = TestEnum::C {
name: String::from("John"),
};
assert_ts_eq(&a.ctor_tokens(), "e! {TestEnum::A});
assert_ts_eq(&b.ctor_tokens(), "e! {TestEnum::B(1i32)});
assert_ts_eq(
&c.ctor_tokens(),
"e! {TestEnum::C { name: String::from("John") }},
);
}
#[test]
fn test_ast() {
#[derive(Quine)]
struct Metadata {
src: String,
}
#[derive(Quine)]
enum Ast {
Num(Box<Metadata>, isize),
Mul(Box<Metadata>, Box<Ast>, Box<Ast>),
Sum(Box<Metadata>, Vec<Ast>),
}
let ast = Ast::Sum(
Box::new(Metadata {
src: String::from("1 + (2 * 3)"),
}),
vec![
Ast::Num(
Box::new(Metadata {
src: String::from("1"),
}),
1,
),
Ast::Mul(
Box::new(Metadata {
src: String::from("2 * 3"),
}),
Box::new(Ast::Num(
Box::new(Metadata {
src: String::from("2"),
}),
2,
)),
Box::new(Ast::Num(
Box::new(Metadata {
src: String::from("3"),
}),
3,
)),
),
],
);
assert_ts_eq(
&ast.ctor_tokens(),
"e! {
Ast::Sum(
Box::new(Metadata {
src: String::from("1 + (2 * 3)")
}),
Vec::from([
Ast::Num(
Box::new(Metadata {
src: String::from("1")
}),
1isize
),
Ast::Mul(
Box::new(Metadata {
src: String::from("2 * 3")
}),
Box::new(Ast::Num(
Box::new(Metadata {
src: String::from("2")
}),
2isize
)),
Box::new(Ast::Num(
Box::new(Metadata {
src: String::from("3")
}),
3isize
))
)
])
)
},
);
}
}