lang/syn: Simplify program parsing
This commit is contained in:
parent
c67dabd1f9
commit
5aeb288b51
|
@ -1,13 +1,15 @@
|
|||
use crate::parser;
|
||||
use crate::{Program, Rpc, RpcArg, State, StateInterface, StateRpc};
|
||||
|
||||
const STATE_STRUCT_ATTRIBUTE: &str = "state";
|
||||
|
||||
pub fn parse(program_mod: syn::ItemMod) -> Program {
|
||||
let mod_ident = &program_mod.ident;
|
||||
|
||||
let mod_content = &program_mod.content.as_ref().unwrap().1;
|
||||
|
||||
// Parse the state struct singleton.
|
||||
// Parse program state.
|
||||
let state: Option<State> = {
|
||||
// Parse `struct` marked with the `#[state]` attribute.
|
||||
let strct: Option<&syn::ItemStruct> = mod_content
|
||||
.iter()
|
||||
.filter_map(|item| match item {
|
||||
|
@ -17,7 +19,7 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
return None;
|
||||
}
|
||||
let attr_label = attrs[0].path.get_ident().map(|i| i.to_string());
|
||||
if attr_label != Some("state".to_string()) {
|
||||
if attr_label != Some(STATE_STRUCT_ATTRIBUTE.to_string()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -26,7 +28,7 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
_ => None,
|
||||
})
|
||||
.next();
|
||||
|
||||
// Parse `impl` block for the state struct.
|
||||
let impl_block: Option<syn::ItemImpl> = match strct {
|
||||
None => None,
|
||||
Some(strct) => mod_content
|
||||
|
@ -47,43 +49,7 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
})
|
||||
.next(),
|
||||
};
|
||||
|
||||
// All program interface implementations.
|
||||
let trait_impls: Option<Vec<StateInterface>> = strct.map(|_strct| {
|
||||
mod_content
|
||||
.iter()
|
||||
.filter_map(|item| match item {
|
||||
syn::Item::Impl(item_impl) => {
|
||||
let trait_name = match &item_impl.trait_ {
|
||||
None => return None,
|
||||
Some((_, path, _)) => path
|
||||
.segments
|
||||
.iter()
|
||||
.next()
|
||||
.expect("Must have one segmeent in a path")
|
||||
.ident
|
||||
.clone()
|
||||
.to_string(),
|
||||
};
|
||||
if item_impl.trait_.is_none() {
|
||||
return None;
|
||||
}
|
||||
let methods = parse_state_trait_methods(item_impl);
|
||||
Some(StateInterface {
|
||||
trait_name,
|
||||
methods,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<StateInterface>>()
|
||||
});
|
||||
|
||||
strct.map(|strct| {
|
||||
// Chop off the `#[state]` attribute. It's just a marker.
|
||||
let mut strct = strct.clone();
|
||||
strct.attrs = vec![];
|
||||
|
||||
// Parse ctor and the type in `Context<MY-TYPE>`.
|
||||
let ctor_and_anchor = match &impl_block {
|
||||
None => None,
|
||||
Some(impl_block) => {
|
||||
|
@ -110,9 +76,9 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
.clone()
|
||||
}
|
||||
};
|
||||
|
||||
let impl_block_and_methods = impl_block.map(|impl_block| {
|
||||
let methods: Vec<StateRpc> = impl_block
|
||||
// Parse all methods in the above `impl` block.
|
||||
let methods: Option<Vec<StateRpc>> = impl_block.as_ref().map(|impl_block| {
|
||||
impl_block
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
|
@ -156,30 +122,106 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
(impl_block.clone(), methods)
|
||||
.collect()
|
||||
});
|
||||
// Parse all trait implementations for the above `#[state]` struct.
|
||||
let trait_impls: Option<Vec<StateInterface>> = strct.map(|_strct| {
|
||||
mod_content
|
||||
.iter()
|
||||
.filter_map(|item| match item {
|
||||
syn::Item::Impl(item_impl) => {
|
||||
let trait_name = match &item_impl.trait_ {
|
||||
None => return None,
|
||||
Some((_, path, _)) => path
|
||||
.segments
|
||||
.iter()
|
||||
.next()
|
||||
.expect("Must have one segment in a path")
|
||||
.ident
|
||||
.clone()
|
||||
.to_string(),
|
||||
};
|
||||
if item_impl.trait_.is_none() {
|
||||
return None;
|
||||
}
|
||||
let methods = item_impl
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => match m.sig.inputs.first() {
|
||||
None => None,
|
||||
Some(_arg) => {
|
||||
let mut has_receiver = false;
|
||||
let mut args = m
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
syn::FnArg::Receiver(_) => {
|
||||
has_receiver = true;
|
||||
None
|
||||
}
|
||||
syn::FnArg::Typed(arg) => Some(arg),
|
||||
})
|
||||
.map(|raw_arg| {
|
||||
let ident = match &*raw_arg.pat {
|
||||
syn::Pat::Ident(ident) => &ident.ident,
|
||||
_ => panic!("invalid syntax"),
|
||||
};
|
||||
RpcArg {
|
||||
name: ident.clone(),
|
||||
raw_arg: raw_arg.clone(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<RpcArg>>();
|
||||
// Remove the Anchor accounts argument
|
||||
let anchor = args.remove(0);
|
||||
let anchor_ident = extract_ident(&anchor.raw_arg).clone();
|
||||
|
||||
Some(StateRpc {
|
||||
raw_method: m.clone(),
|
||||
ident: m.sig.ident.clone(),
|
||||
args,
|
||||
anchor_ident,
|
||||
has_receiver,
|
||||
})
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
Some(StateInterface {
|
||||
trait_name,
|
||||
methods,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<StateInterface>>()
|
||||
});
|
||||
// Put it all together.
|
||||
strct.map(|strct| {
|
||||
// Chop off the `#[state]` attribute. It's just a marker.
|
||||
let mut strct = strct.clone();
|
||||
strct.attrs = vec![];
|
||||
|
||||
State {
|
||||
name: strct.ident.to_string(),
|
||||
strct: strct.clone(),
|
||||
interfaces: trait_impls,
|
||||
impl_block_and_methods,
|
||||
impl_block_and_methods: impl_block
|
||||
.map(|impl_block| (impl_block.clone(), methods.unwrap())),
|
||||
ctor_and_anchor,
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let methods: Vec<&syn::ItemFn> = mod_content
|
||||
// Parse all non-state instruction handlers.
|
||||
let rpcs: Vec<Rpc> = mod_content
|
||||
.iter()
|
||||
.filter_map(|item| match item {
|
||||
syn::Item::Fn(item_fn) => Some(item_fn),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let rpcs: Vec<Rpc> = methods
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|method: &syn::ItemFn| {
|
||||
let mut args: Vec<RpcArg> = method
|
||||
.sig
|
||||
|
@ -199,7 +241,7 @@ pub fn parse(program_mod: syn::ItemMod) -> Program {
|
|||
_ => panic!("invalid syntax"),
|
||||
})
|
||||
.collect();
|
||||
// Remove the Anchor accounts argument
|
||||
// Remove the Context argument
|
||||
let anchor = args.remove(0);
|
||||
let anchor_ident = extract_ident(&anchor.raw_arg).clone();
|
||||
|
||||
|
@ -245,52 +287,3 @@ fn extract_ident(path_ty: &syn::PatType) -> &proc_macro2::Ident {
|
|||
};
|
||||
&path.segments[0].ident
|
||||
}
|
||||
|
||||
fn parse_state_trait_methods(item_impl: &syn::ItemImpl) -> Vec<StateRpc> {
|
||||
item_impl
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|item: &syn::ImplItem| match item {
|
||||
syn::ImplItem::Method(m) => match m.sig.inputs.first() {
|
||||
None => None,
|
||||
Some(_arg) => {
|
||||
let mut has_receiver = false;
|
||||
let mut args = m
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
syn::FnArg::Receiver(_) => {
|
||||
has_receiver = true;
|
||||
None
|
||||
}
|
||||
syn::FnArg::Typed(arg) => Some(arg),
|
||||
})
|
||||
.map(|raw_arg| {
|
||||
let ident = match &*raw_arg.pat {
|
||||
syn::Pat::Ident(ident) => &ident.ident,
|
||||
_ => panic!("invalid syntax"),
|
||||
};
|
||||
RpcArg {
|
||||
name: ident.clone(),
|
||||
raw_arg: raw_arg.clone(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<RpcArg>>();
|
||||
// Remove the Anchor accounts argument
|
||||
let anchor = args.remove(0);
|
||||
let anchor_ident = extract_ident(&anchor.raw_arg).clone();
|
||||
|
||||
Some(StateRpc {
|
||||
raw_method: m.clone(),
|
||||
ident: m.sig.ident.clone(),
|
||||
args,
|
||||
anchor_ident,
|
||||
has_receiver,
|
||||
})
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue