proc: simplify expose macro

This commit is contained in:
🪞👃🪞 2025-05-06 00:48:14 +03:00
parent cba23a005c
commit 7570aefcc2
5 changed files with 288 additions and 405 deletions

View file

@ -8,6 +8,7 @@ edition = { workspace = true }
proc-macro = true
[dependencies]
syn = { version = "2", features = ["full", "extra-traits"] }
quote = { version = "1" }
syn = { version = "2", features = ["full", "extra-traits"] }
quote = { version = "1" }
proc-macro2 = { version = "1", features = ["span-locations"] }
heck = { version = "0.5" }

View file

@ -1,6 +1,6 @@
extern crate proc_macro;
pub(crate) use std::collections::{HashMap, BTreeMap};
pub(crate) use std::collections::{BTreeMap, BTreeSet};
pub(crate) use std::cmp::Ordering;
pub(crate) use proc_macro::TokenStream;
pub(crate) use proc_macro2::{
@ -10,22 +10,38 @@ pub(crate) use proc_macro2::{
pub(crate) use syn::{
parse, parse_macro_input, parse_quote as pq,
braced, bracketed, parenthesized, Token,
Arm, Expr, Attribute, Meta, MetaList, Path, PathSegment, PathArguments, ImplItem, LitStr, Type,
Arm, Expr, Attribute, Meta, MetaList, Path, PathSegment, PathArguments,
ImplItem, ImplItemFn, LitStr, Type, ItemImpl, ReturnType, Signature,
parse::{Parse, ParseStream, Result},
token::{PathSep, Brace},
punctuated::Punctuated,
};
pub(crate) use quote::{quote, TokenStreamExt, ToTokens};
pub(crate) use heck::AsKebabCase;
mod proc_view;
mod proc_expose;
#[proc_macro_attribute]
pub fn view (meta: TokenStream, item: TokenStream) -> TokenStream {
self::proc_view::view_impl(meta.into(), item.into()).into()
use self::proc_view::{ViewDef, ViewMeta, ViewImpl};
write_macro(ViewDef(
parse_macro_input!(meta as ViewMeta),
parse_macro_input!(item as ViewImpl),
))
}
#[proc_macro_attribute]
pub fn expose (meta: TokenStream, item: TokenStream) -> TokenStream {
self::proc_expose::expose_impl(meta.into(), item.into()).into()
use self::proc_expose::{ExposeDef, ExposeMeta, ExposeImpl};
write_macro(ExposeDef(
parse_macro_input!(meta as ExposeMeta),
parse_macro_input!(item as ExposeImpl),
))
}
fn write_macro <T: ToTokens> (t: T) -> TokenStream {
let mut out = TokenStream2::new();
t.to_tokens(&mut out);
out.into()
}

View file

@ -1,103 +1,76 @@
use crate::*;
use syn::parse::discouraged::Speculative;
pub(crate) fn expose_impl (meta: TokenStream, data: TokenStream) -> TokenStream {
let mut out = TokenStream2::new();
ExposeDefinition {
meta: parse_macro_input!(meta as ExposeMeta),
data: parse_macro_input!(data as ExposeImpl),
}.to_tokens(&mut out);
out.into()
#[derive(Debug, Clone)]
pub(crate) struct ExposeDef(pub(crate) ExposeMeta, pub(crate) ExposeImpl);
#[derive(Debug, Clone)]
pub(crate) struct ExposeMeta;
#[derive(Debug, Clone)]
pub(crate) struct ExposeImpl {
block: ItemImpl,
exposed: BTreeMap<ExposeType, BTreeMap<String, Ident>>,
}
#[derive(Debug, Clone)]
struct ExposeDefinition {
meta: ExposeMeta,
data: ExposeImpl,
}
struct ExposeArm(String, Ident);
impl Parse for ExposeDefinition {
#[derive(Debug, Clone)]
struct ExposeSym(LitStr);
#[derive(Debug, Clone)]
struct ExposeType(Box<Type>);
impl Parse for ExposeMeta {
fn parse (input: ParseStream) -> Result<Self> {
Ok(Self {
meta: input.parse::<ExposeMeta>()?,
data: input.parse::<ExposeImpl>()?,
})
Ok(Self)
}
}
impl ToTokens for ExposeDefinition {
impl Parse for ExposeImpl {
fn parse (input: ParseStream) -> Result<Self> {
let block = input.parse::<ItemImpl>()?;
let mut exposed: BTreeMap<ExposeType, BTreeMap<String, Ident>> = Default::default();
for item in block.items.iter() {
if let ImplItem::Fn(ImplItemFn { sig: Signature { ident, output, .. }, .. }) = item {
if let ReturnType::Type(_, return_type) = output {
let return_type = ExposeType(return_type.clone());
if !exposed.contains_key(&return_type) {
exposed.insert(return_type.clone(), Default::default());
}
let values = exposed.get_mut(&return_type).unwrap();
let key = format!(":{}", AsKebabCase(format!("{}", &ident)));
if values.contains_key(&key) {
return Err(input.error(format!("already defined: {key}")))
}
values.insert(key, ident.clone());
} else {
return Err(input.error("output type must be specified"))
}
}
}
Ok(Self { block, exposed })
}
}
impl ToTokens for ExposeDef {
fn to_tokens (&self, out: &mut TokenStream2) {
let Self { meta, data } = self;
let Self(meta, data) = self;
for token in quote! { #data } {
out.append(token)
}
}
}
#[derive(Debug, Clone)]
struct ExposeMeta {}
impl Parse for ExposeMeta {
fn parse (input: ParseStream) -> Result<Self> {
Ok(Self {})
}
}
#[derive(Debug, Clone)]
struct ExposeImpl {
target: Ident,
items: Vec<ImplItem>,
types: BTreeMap<ExposeType, BTreeMap<ExposeSym, ExposeArm>>,
}
impl Parse for ExposeImpl {
fn parse (input: ParseStream) -> Result<Self> {
let _impl = input.parse::<Token![impl]>()?;
let target = input.parse::<Ident>()?;
let group;
let brace = braced!(group in input);
let mut items = vec![];
let mut types: BTreeMap<ExposeType, BTreeMap<ExposeSym, ExposeArm>> = Default::default();
while !group.is_empty() {
let fork = group.fork();
if let Ok(block) = fork.parse::<ExposeBlock>() {
let t = block.type_.into();
if let Some(values) = types.get_mut(&t) {
for (key, value) in block.values.into_iter() {
if values.contains_key(&key) {
return Err(input.error(format!("{key:?} ({t:?}): already exists")))
} else {
values.insert(key, value);
}
}
} else {
types.insert(t, block.values);
}
group.advance_to(&fork);
continue
}
let fork = group.fork();
if let Ok(item) = fork.parse::<ImplItem>() {
items.push(item);
group.advance_to(&fork);
continue
}
return Err(input.error(
"expected either item or #[tengri::expose(type)] { \":key\" => value }"
));
}
Ok(Self { target, items, types })
}
}
impl ToTokens for ExposeImpl {
fn to_tokens (&self, out: &mut TokenStream2) {
let Self { target, items, types } = self;
for token in quote! { impl #target { #(#items)* } } {
let Self { block, exposed } = self;
let target = &self.block.self_ty;
for token in quote! { #block } {
out.append(token);
}
for (t, variants) in types.iter() {
let predef = match format!("{}", quote! { #t }).as_str() {
for (t, variants) in exposed.iter() {
let predefined = match format!("{}", quote! { #t }).as_str() {
"bool" => vec![
quote! { ::tengri::dsl::Value::Sym(":true") => true },
quote! { ::tengri::dsl::Value::Sym(":false") => false },
@ -113,7 +86,7 @@ impl ToTokens for ExposeImpl {
impl ::tengri::dsl::Context<#t> for #target {
fn get (&self, dsl: &::tengri::dsl::Value) -> Option<#t> {
Some(match dsl {
#(#predef,)*
#(#predefined,)*
#(#values,)*
_ => return None
})
@ -127,61 +100,9 @@ impl ToTokens for ExposeImpl {
}
}
#[derive(Debug, Clone)]
struct ExposeBlock {
type_: Type,
values: BTreeMap<ExposeSym, ExposeArm>,
}
impl Parse for ExposeBlock {
fn parse (input: ParseStream) -> Result<Self> {
let _ = input.parse::<Token![#]>()?;
let group;
let bracket = bracketed!(group in input);
let path = group.parse::<Path>()?;
let type_ = if
path.segments.get(0).map(|x|x.ident.to_string()) == Some("tengri".to_string()) &&
path.segments.get(1).map(|x|x.ident.to_string()) == Some("expose".to_string())
{
let token;
let paren = parenthesized!(token in group);
token.parse::<Type>()?
} else {
return Err(input.error("expected #[tengri::expose(type)]"))
};
let group;
let brace = braced!(group in input);
let mut values = BTreeMap::new();
while !group.is_empty() {
let arm = group.parse::<ExposeArm>()?;
values.insert(arm.key.clone(), arm);
let _ = group.parse::<Token![,]>()?;
}
Ok(Self { type_, values })
}
}
#[derive(Debug, Clone)]
struct ExposeArm {
key: ExposeSym,
value: Expr
}
impl Parse for ExposeArm {
fn parse (input: ParseStream) -> Result<Self> {
let key = input.parse::<LitStr>()?.into();
let _ = input.parse::<Token![=]>()?;
let _ = input.parse::<Token![>]>()?;
let value = input.parse::<Expr>()?;
Ok(Self { key, value })
}
}
impl ToTokens for ExposeArm {
fn to_tokens (&self, out: &mut TokenStream2) {
let Self { key, value } = self;
let Self(key, value) = self;
out.append(Punct::new(':', Joint));
out.append(Punct::new(':', Alone));
out.append(Ident::new("tengri", Span::call_site()));
@ -196,7 +117,7 @@ impl ToTokens for ExposeArm {
out.append(Ident::new("Sym", Span::call_site()));
out.append(Group::new(Delimiter::Parenthesis, {
let mut out = TokenStream2::new();
out.append(key.0.token());
out.append(LitStr::new(&key, Span::call_site()).token());
out
}));
out.append(Punct::new('=', Joint));
@ -207,9 +128,6 @@ impl ToTokens for ExposeArm {
}
}
#[derive(Debug, Clone)]
struct ExposeSym(LitStr);
impl From<LitStr> for ExposeSym {
fn from (this: LitStr) -> Self {
Self(this)
@ -242,12 +160,9 @@ impl PartialEq for ExposeSym {
impl Eq for ExposeSym {}
#[derive(Debug, Clone)]
struct ExposeType(Type);
impl From<Type> for ExposeType {
fn from (this: Type) -> Self {
Self(this)
Self(Box::new(this))
}
}
@ -284,109 +199,107 @@ impl ToTokens for ExposeType {
}
#[cfg(test)] #[test] fn test_expose_definition () {
let parsed: ExposeImpl = pq! {
//#[tengri_proc::expose]
impl Something {
#[tengri::expose(bool)] {
":bool1" => true || false,
}
fn something () {}
}
};
// FIXME:
//assert_eq!(
//format!("{}", quote! { #parsed }),
//format!("{}", quote! {
//impl Something {
//fn something () {}
//}
//impl ::tengri::Context<bool> for Something {
//fn get (&self, dsl: &::tengri::Value) -> Option<bool> {
//Some(match dsl {
//::tengri::Value::Sym(":true") => true,
//::tengri::Value::Sym(":false") => false,
//::tengri::Value::Sym(":bool1") => true || false,
//_ => return None
//})
//}
//}
//})
//);
// TODO
//let parsed: ExposeImpl = pq! {
////#[tengri_proc::expose]
//impl Something {
//fn something () -> bool {}
//}
//};
//// FIXME:
////assert_eq!(
////format!("{}", quote! { #parsed }),
////format!("{}", quote! {
////impl Something {
////fn something () {}
////}
////impl ::tengri::Context<bool> for Something {
////fn get (&self, dsl: &::tengri::Value) -> Option<bool> {
////Some(match dsl {
////::tengri::Value::Sym(":true") => true,
////::tengri::Value::Sym(":false") => false,
////::tengri::Value::Sym(":bool1") => true || false,
////_ => return None
////})
////}
////}
////})
////);
let parsed: ExposeImpl = pq! {
//#[tengri_proc::expose]
impl Something {
#[tengri::expose(bool)] {
":bool1" => true || false,
}
#[tengri::expose(u16)] {
":u161" => 0 + 1,
}
#[tengri::expose(usize)] {
":usize1" => 1 + 2,
}
#[tengri::expose(Arc<str>)] {
":arcstr1" => "foo".into(),
}
#[tengri::expose(Option<Arc<str>>)] {
":optarcstr1" => Some("bar".into()),
":optarcstr2" => Some("baz".into()),
}
fn something () {}
}
};
// FIXME:
//assert_eq!(
//format!("{}", quote! { #parsed }),
//format!("{}", quote! {
//impl Something {
//fn something () {}
//let parsed: ExposeImpl = pq! {
////#[tengri_proc::expose]
//impl Something {
//#[tengri::expose(bool)] {
//":bool1" => true || false,
//}
//impl ::tengri::Context<Arc<str>> for Something {
//fn get (&self, dsl: &::tengri::Value) -> Option<Arc<str>> {
//Some(match dsl {
//::tengri::Value::Sym(":arcstr1") => "foo".into(),
//_ => return None
//})
//}
//#[tengri::expose(u16)] {
//":u161" => 0 + 1,
//}
//impl ::tengri::Context<Option<Arc<str>>> for Something {
//fn get (&self, dsl: &::tengri::Value) -> Option<Option<Arc<str>>> {
//Some(match dsl {
//::tengri::Value::Sym(":optarcstr1") => Some("bar".into()),
//::tengri::Value::Sym(":optarcstr2") => Some("baz".into()),
//_ => return None
//})
//}
//#[tengri::expose(usize)] {
//":usize1" => 1 + 2,
//}
//impl ::tengri::Context<bool> for Something {
//fn get (&self, dsl: &::tengri::Value) -> Option<bool> {
//Some(match dsl {
//::tengri::Value::Sym(":true") => true,
//::tengri::Value::Sym(":false") => false,
//::tengri::Value::Sym(":bool1") => true || false,
//_ => return None
//})
//}
//#[tengri::expose(Arc<str>)] {
//":arcstr1" => "foo".into(),
//}
//impl ::tengri::Context<u16> for Something {
//fn get (&self, dsl: &::tengri::Value) -> Option<u16> {
//Some(match dsl {
//::tengri::Value::Num(n) => *n as u16,
//::tengri::Value::Sym(":u161") => 0 + 1,
//_ => return None
//})
//}
//#[tengri::expose(Option<Arc<str>>)] {
//":optarcstr1" => Some("bar".into()),
//":optarcstr2" => Some("baz".into()),
//}
//impl ::tengri::Context<usize> for Something {
//fn get (&self, dsl: &::tengri::Value) -> Option<usize> {
//Some(match dsl {
//::tengri::Value::Num(n) => *n as usize,
//::tengri::Value::Sym(":usize1") => 1 + 2,
//_ => return None
//})
//}
//}
//})
//)
//fn something () {}
//}
//};
//// FIXME:
////assert_eq!(
////format!("{}", quote! { #parsed }),
////format!("{}", quote! {
////impl Something {
////fn something () {}
////}
////impl ::tengri::Context<Arc<str>> for Something {
////fn get (&self, dsl: &::tengri::Value) -> Option<Arc<str>> {
////Some(match dsl {
////::tengri::Value::Sym(":arcstr1") => "foo".into(),
////_ => return None
////})
////}
////}
////impl ::tengri::Context<Option<Arc<str>>> for Something {
////fn get (&self, dsl: &::tengri::Value) -> Option<Option<Arc<str>>> {
////Some(match dsl {
////::tengri::Value::Sym(":optarcstr1") => Some("bar".into()),
////::tengri::Value::Sym(":optarcstr2") => Some("baz".into()),
////_ => return None
////})
////}
////}
////impl ::tengri::Context<bool> for Something {
////fn get (&self, dsl: &::tengri::Value) -> Option<bool> {
////Some(match dsl {
////::tengri::Value::Sym(":true") => true,
////::tengri::Value::Sym(":false") => false,
////::tengri::Value::Sym(":bool1") => true || false,
////_ => return None
////})
////}
////}
////impl ::tengri::Context<u16> for Something {
////fn get (&self, dsl: &::tengri::Value) -> Option<u16> {
////Some(match dsl {
////::tengri::Value::Num(n) => *n as u16,
////::tengri::Value::Sym(":u161") => 0 + 1,
////_ => return None
////})
////}
////}
////impl ::tengri::Context<usize> for Something {
////fn get (&self, dsl: &::tengri::Value) -> Option<usize> {
////Some(match dsl {
////::tengri::Value::Num(n) => *n as usize,
////::tengri::Value::Sym(":usize1") => 1 + 2,
////_ => return None
////})
////}
////}
////})
////)
}

View file

@ -1,52 +1,20 @@
use crate::*;
pub(crate) fn view_impl (meta: TokenStream, data: TokenStream) -> TokenStream {
let mut out = TokenStream2::new();
ViewDefinition {
meta: parse_macro_input!(meta as ViewMeta),
data: parse_macro_input!(data as ViewImpl),
}.to_tokens(&mut out);
out.into()
}
#[derive(Debug, Clone)]
pub(crate) struct ViewDef(pub(crate) ViewMeta, pub(crate) ViewImpl);
#[derive(Debug, Clone)]
struct ViewDefinition {
meta: ViewMeta,
data: ViewImpl,
}
#[derive(Debug, Clone)]
struct ViewMeta {
pub(crate) struct ViewMeta {
output: Ident,
}
#[derive(Debug, Clone)]
struct ViewImpl {
target: Ident,
items: Vec<ViewItem>,
syms: Vec<ViewSym>,
pub(crate) struct ViewImpl {
block: ItemImpl,
exposed: BTreeMap<String, Ident>,
}
#[derive(Debug, Clone)]
struct ViewItem {
item: ImplItem,
expose: Option<Literal>,
}
#[derive(Debug, Clone)]
struct ViewSym {
symbol: Literal,
name: Ident,
}
impl Parse for ViewDefinition {
fn parse (input: ParseStream) -> Result<Self> {
Ok(Self {
meta: input.parse::<ViewMeta>()?,
data: input.parse::<ViewImpl>()?,
})
}
}
struct ViewArm(String, Ident);
impl Parse for ViewMeta {
fn parse (input: ParseStream) -> Result<Self> {
@ -58,68 +26,52 @@ impl Parse for ViewMeta {
impl Parse for ViewImpl {
fn parse (input: ParseStream) -> Result<Self> {
let _ = input.parse::<Token![impl]>()?;
let mut syms = vec![];
Ok(Self {
target: input.parse::<Ident>()?,
items: {
let group;
let brace = braced!(group in input);
let mut items = vec![];
while !group.is_empty() {
let item = group.parse::<ViewItem>()?;
if let Some(expose) = &item.expose {
if let ImplItem::Fn(ref item) = item.item {
let symbol = expose.clone();
let name = item.sig.ident.clone();
syms.push(ViewSym { symbol, name })
} else {
return Err(
input.error("only fn items can be exposed to #[tengri::view]")
)
}
}
items.push(item);
let block = input.parse::<ItemImpl>()?;
let mut exposed: BTreeMap<String, Ident> = Default::default();
for item in block.items.iter() {
if let ImplItem::Fn(ImplItemFn { sig: Signature { ident, .. }, .. }) = item {
let key = format!(":{}", AsKebabCase(format!("{}", &ident)));
if exposed.contains_key(&key) {
return Err(input.error(format!("already defined: {ident}")));
}
items
},
syms,
})
exposed.insert(key, ident.clone());
}
}
Ok(Self { block, exposed })
}
}
impl Parse for ViewItem {
fn parse (input: ParseStream) -> Result<Self> {
let mut expose = None;
Ok(Self {
item: {
let mut item = input.parse::<ImplItem>()?;
if let ImplItem::Fn(ref mut item) = item {
item.attrs = item.attrs.iter().filter(|attr| {
if let Attribute {
meta: Meta::List(MetaList { path, tokens, .. }), ..
} = attr
&& path.segments.len() == 2
&& nth_segment_is(&path.segments, 0, "tengri")
&& nth_segment_is(&path.segments, 1, "view")
&& let Some(TokenTree::Literal(name)) = tokens.clone().into_iter().next()
{
expose = Some(name);
return false
}
true
}).map(|x|x.clone()).collect();
};
item
},
expose,
})
}
}
impl ToTokens for ViewSym {
impl ToTokens for ViewDef {
fn to_tokens (&self, out: &mut TokenStream2) {
let Self(ViewMeta { output }, ViewImpl { block, exposed }) = self;
let ident = &block.self_ty;
let exposed: Vec<_> = exposed.iter().map(|(k,v)|ViewArm(k.clone(), v.clone())).collect();
for token in quote! {
#block
/// Generated by [tengri_proc].
impl ::tengri::output::Content<#output> for #ident {
fn content (&self) -> impl Render<#output> {
self.size.of(::tengri::output::View(self, self.config.view))
}
}
/// Generated by [tengri_proc].
impl<'a> ::tengri::output::ViewContext<'a, #output> for #ident {
fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, #output>> {
match value {
#(#exposed)*
_ => panic!("expected Sym(content), got: {value:?}")
}
}
}
} {
out.append(token)
}
}
}
impl ToTokens for ViewArm {
fn to_tokens (&self, out: &mut TokenStream2) {
let Self(key, value) = self;
out.append(Punct::new(':', Joint));
out.append(Punct::new(':', Alone));
out.append(Ident::new("tengri", Span::call_site()));
@ -134,7 +86,7 @@ impl ToTokens for ViewSym {
out.append(Ident::new("Sym", Span::call_site()));
out.append(Group::new(Delimiter::Parenthesis, {
let mut out = TokenStream2::new();
out.append(self.symbol.clone());
out.append(LitStr::new(key, Span::call_site()).token());
out
}));
out.append(Punct::new('=', Joint));
@ -144,7 +96,7 @@ impl ToTokens for ViewSym {
let mut out = TokenStream2::new();
out.append(Ident::new("self", Span::call_site()));
out.append(Punct::new('.', Alone));
out.append(self.name.clone());
out.append(value.clone());
out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new()));
out.append(Punct::new('.', Alone));
out.append(Ident::new("boxed", Span::call_site()));
@ -155,43 +107,42 @@ impl ToTokens for ViewSym {
}
}
impl ToTokens for ViewItem {
fn to_tokens (&self, out: &mut TokenStream2) {
self.item.to_tokens(out)
}
}
impl ToTokens for ViewDefinition {
fn to_tokens (&self, out: &mut TokenStream2) {
let Self {
meta: ViewMeta { output },
data: ViewImpl { target, syms, items },
} = self;
for token in quote! {
/// Augmented by [tengri_proc].
impl #target {
#(#items)*
}
/// Generated by [tengri_proc].
impl ::tengri::output::Content<#output> for #target {
fn content (&self) -> impl Render<#output> {
self.size.of(::tengri::output::View(self, self.config.view))
}
}
/// Generated by [tengri_proc].
impl<'a> ::tengri::output::ViewContext<'a, #output> for #target {
fn get_content_sym (&'a self, value: &Value<'a>) -> Option<RenderBox<'a, #output>> {
match value {
#(#syms)*
_ => panic!("expected Sym(content), got: {value:?}")
}
}
}
} {
out.append(token)
}
}
}
//impl ToTokens for ViewSym {
//fn to_tokens (&self, out: &mut TokenStream2) {
//out.append(Punct::new(':', Joint));
//out.append(Punct::new(':', Alone));
//out.append(Ident::new("tengri", Span::call_site()));
//out.append(Punct::new(':', Joint));
//out.append(Punct::new(':', Alone));
//out.append(Ident::new("dsl", Span::call_site()));
//out.append(Punct::new(':', Joint));
//out.append(Punct::new(':', Alone));
//out.append(Ident::new("Value", Span::call_site()));
//out.append(Punct::new(':', Joint));
//out.append(Punct::new(':', Alone));
//out.append(Ident::new("Sym", Span::call_site()));
//out.append(Group::new(Delimiter::Parenthesis, {
//let mut out = TokenStream2::new();
//out.append(self.symbol.clone());
//out
//}));
//out.append(Punct::new('=', Joint));
//out.append(Punct::new('>', Alone));
//out.append(Ident::new("Some", Span::call_site()));
//out.append(Group::new(Delimiter::Parenthesis, {
//let mut out = TokenStream2::new();
//out.append(Ident::new("self", Span::call_site()));
//out.append(Punct::new('.', Alone));
//out.append(self.name.clone());
//out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new()));
//out.append(Punct::new('.', Alone));
//out.append(Ident::new("boxed", Span::call_site()));
//out.append(Group::new(Delimiter::Parenthesis, TokenStream2::new()));
//out
//}));
//out.append(Punct::new(',', Alone));
//}
//}
fn nth_segment_is (segments: &Punctuated<PathSegment, PathSep>, n: usize, x: &str) -> bool {
if let Some(PathSegment { arguments: PathArguments::None, ident, .. }) = segments.get(n) {
@ -202,17 +153,17 @@ fn nth_segment_is (segments: &Punctuated<PathSegment, PathSep>, n: usize, x: &st
return false
}
impl std::cmp::PartialEq for ViewItem {
fn eq (&self, other: &Self) -> bool {
self.item == other.item && (format!("{:?}", self.expose) == format!("{:?}", other.expose))
}
}
//impl std::cmp::PartialEq for ViewItem {
//fn eq (&self, other: &Self) -> bool {
//self.item == other.item && (format!("{:?}", self.expose) == format!("{:?}", other.expose))
//}
//}
impl std::cmp::PartialEq for ViewSym {
fn eq (&self, other: &Self) -> bool {
self.name == other.name && (format!("{}", self.symbol) == format!("{}", other.symbol))
}
}
//impl std::cmp::PartialEq for ViewSym {
//fn eq (&self, other: &Self) -> bool {
//self.name == other.name && (format!("{}", self.symbol) == format!("{}", other.symbol))
//}
//}
#[cfg(test)] #[test] fn test_view_meta () {
let x: ViewMeta = pq! { SomeOutput };
@ -221,6 +172,7 @@ impl std::cmp::PartialEq for ViewSym {
}
#[cfg(test)] #[test] fn test_view_impl () {
// TODO
let x: ViewImpl = pq! {
impl Foo {
/// docstring1
@ -232,20 +184,20 @@ impl std::cmp::PartialEq for ViewSym {
}
};
let expected_target: Ident = pq! { Foo };
assert_eq!(x.target, expected_target);
assert_eq!(x.items.len(), 2);
assert_eq!(x.items[0].item, pq! {
/// docstring1
#[bar] fn a_view () {}
});
assert_eq!(x.items[1].item, pq! {
#[baz]
/// docstring2
#[baz] fn is_not_view () {}
});
assert_eq!(x.syms, vec![
ViewSym { symbol: pq! { ":view1" }, name: pq! { a_view }, },
]);
//assert_eq!(x.target, expected_target);
//assert_eq!(x.items.len(), 2);
//assert_eq!(x.items[0].item, pq! {
///// docstring1
//#[bar] fn a_view () {}
//});
//assert_eq!(x.items[1].item, pq! {
//#[baz]
///// docstring2
//#[baz] fn is_not_view () {}
//});
//assert_eq!(x.syms, vec![
//ViewArm( { symbol: pq! { ":view1" }, name: pq! { a_view }, },
//]);
}
#[cfg(test)] #[test] fn test_view_definition () {