use std::rc::Rc;
use quote::ToTokens;
use super::*;
mod acceptance;
mod roundtrip;
fn lower(ty: proc_macro2::TokenStream) -> Result<TypeRef, UnsupportedType> {
let item: syn::Item = syn::parse_quote!(
pub struct S {
pub f: #ty,
}
);
let mut items = fixture_types();
items.push(tag_len_const());
items.push(opaque("Sample"));
let n = items.len();
items.push(item);
match parse(items).remove(n) {
Element::Type(Type::Struct(s)) => Ok(s.fields[0].ty.clone()),
Element::Unsupported(u) => match *u.error {
ItemError::FieldType { source, .. } => Err(source),
other => panic!("expected a field-type diagnosis, got {other}"),
},
other => panic!("expected a struct, got {}", describe(&other)),
}
}
fn parse_one(item: syn::Item) -> Element {
let mut items = fixture_types();
let n = items.len();
items.push(item);
let mut out = parse(items);
assert_eq!(out.len(), n + 1);
out.remove(n)
}
fn fixture_types() -> Vec<syn::Item> {
[
"Error",
"KeyExpr",
"Foo",
"Whatever",
"SomethingUnexpressible",
]
.into_iter()
.map(opaque)
.collect()
}
fn parse(items: Vec<syn::Item>) -> Vec<Element> {
try_parse(items).expect("stream parses")
}
fn try_parse(items: Vec<syn::Item>) -> Result<Vec<Element>, ParseError> {
Flat::builder()
.items(items.into_iter().map(|i| (i, loc())))
.build()
.map(|flat| flat.elements().cloned().collect())
}
fn loc() -> SourceLocation {
SourceLocation {
file: "src/lib.rs".to_string(),
line: 1,
column: 1,
crate_name: Some("myflat".to_string()),
}
}
fn tag_len_const() -> syn::Item {
syn::parse_quote!(
pub const TAG_LEN: usize = 4;
)
}
fn opaque(name: &str) -> syn::Item {
let ident = quote::format_ident!("{name}");
syn::parse_quote!(
pub type #ident = other::#ident;
)
}
fn tokens(t: &impl ToTokens) -> String {
t.to_token_stream().to_string()
}
fn as_fn(e: &Element) -> &Function {
match e {
Element::Function(f) => f,
other => panic!("expected a function, got {}", describe(other)),
}
}
fn as_type(e: &Element) -> &Type {
match e {
Element::Type(t) => t,
other => panic!("expected a type, got {}", describe(other)),
}
}
fn as_struct(e: &Element) -> &Struct {
match as_type(e) {
Type::Struct(s) => s,
other => panic!("expected a struct, got {}", describe_type(other)),
}
}
fn as_enum(e: &Element) -> &Enum {
match as_type(e) {
Type::Enum(en) => en,
other => panic!("expected a fieldless enum, got {}", describe_type(other)),
}
}
fn as_variant(e: &Element) -> &Variant {
match as_type(e) {
Type::Variant(v) => v,
other => panic!("expected a sum, got {}", describe_type(other)),
}
}
fn as_extern(e: &Element) -> &Extern {
match as_type(e) {
Type::Extern(e) => e,
other => panic!("expected an extern, got {}", describe_type(other)),
}
}
fn as_const(e: &Element) -> &Constant {
match e {
Element::Constant(c) => c,
other => panic!("expected a constant, got {}", describe(other)),
}
}
fn as_unsupported(e: &Element) -> &ItemError {
match e {
Element::Unsupported(u) => &u.error,
other => panic!("expected an unsupported item, got {}", describe(other)),
}
}
fn describe(e: &Element) -> String {
match e {
Element::Function(f) => format!("function `{}`", f.name),
Element::Type(t) => describe_type(t),
Element::Constant(c) => format!("constant `{}`", c.name),
Element::Guard(_) => "guard".to_string(),
Element::Unsupported(u) => match &u.name {
Some(name) => format!("unsupported `{name}` ({})", u.error),
None => format!("unsupported ({})", u.error),
},
}
}
fn describe_type(t: &Type) -> String {
let kind = match t {
Type::Struct(_) => "struct",
Type::Variant(_) => "sum",
Type::Enum(_) => "enum",
Type::Extern(_) => "extern",
};
format!("{kind} `{}`", t.name())
}