orchestra_proc_macro/parse/
parse_orchestra_attr.rs

1// Copyright (C) 2022 Parity Technologies (UK) Ltd.
2// SPDX-License-Identifier: Apache-2.0
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::kw;
17use proc_macro2::Span;
18use quote::{quote, ToTokens};
19use std::collections::{hash_map::RandomState, HashMap};
20use syn::{
21	parse::{Parse, ParseBuffer},
22	punctuated::Punctuated,
23	spanned::Spanned,
24	Error, Ident, LitBool, LitInt, Path, Result, Token,
25};
26
27#[derive(Clone, Debug)]
28enum OrchestraAttrItem {
29	ExternEventType { tag: kw::event, eq_token: Token![=], value: Path },
30	ExternOrchestraSignalType { tag: kw::signal, eq_token: Token![=], value: Path },
31	ExternErrorType { tag: kw::error, eq_token: Token![=], value: Path },
32	OutgoingType { tag: kw::outgoing, eq_token: Token![=], value: Path },
33	MessageWrapperName { tag: kw::gen, eq_token: Token![=], value: Ident },
34	BoxedMessages { tag: kw::boxed_messages, eq_token: Token![=], value: bool },
35	SignalChannelCapacity { tag: kw::signal_capacity, eq_token: Token![=], value: usize },
36	MessageChannelCapacity { tag: kw::message_capacity, eq_token: Token![=], value: usize },
37}
38
39impl ToTokens for OrchestraAttrItem {
40	fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
41		let ts = match self {
42			Self::ExternEventType { tag, eq_token, value } => {
43				quote! { #tag #eq_token, #value }
44			},
45			Self::ExternOrchestraSignalType { tag, eq_token, value } => {
46				quote! { #tag #eq_token, #value }
47			},
48			Self::ExternErrorType { tag, eq_token, value } => {
49				quote! { #tag #eq_token, #value }
50			},
51			Self::OutgoingType { tag, eq_token, value } => {
52				quote! { #tag #eq_token, #value }
53			},
54			Self::MessageWrapperName { tag, eq_token, value } => {
55				quote! { #tag #eq_token, #value }
56			},
57			Self::SignalChannelCapacity { tag, eq_token, value } => {
58				quote! { #tag #eq_token, #value }
59			},
60			Self::MessageChannelCapacity { tag, eq_token, value } => {
61				quote! { #tag #eq_token, #value }
62			},
63			Self::BoxedMessages { tag, eq_token, value } => {
64				quote! { #tag #eq_token, #value }
65			},
66		};
67		tokens.extend(ts.into_iter());
68	}
69}
70
71impl Parse for OrchestraAttrItem {
72	fn parse(input: &ParseBuffer) -> Result<Self> {
73		let lookahead = input.lookahead1();
74		if lookahead.peek(kw::event) {
75			Ok(OrchestraAttrItem::ExternEventType {
76				tag: input.parse::<kw::event>()?,
77				eq_token: input.parse()?,
78				value: input.parse()?,
79			})
80		} else if lookahead.peek(kw::signal) {
81			Ok(OrchestraAttrItem::ExternOrchestraSignalType {
82				tag: input.parse::<kw::signal>()?,
83				eq_token: input.parse()?,
84				value: input.parse()?,
85			})
86		} else if lookahead.peek(kw::error) {
87			Ok(OrchestraAttrItem::ExternErrorType {
88				tag: input.parse::<kw::error>()?,
89				eq_token: input.parse()?,
90				value: input.parse()?,
91			})
92		} else if lookahead.peek(kw::outgoing) {
93			Ok(OrchestraAttrItem::OutgoingType {
94				tag: input.parse::<kw::outgoing>()?,
95				eq_token: input.parse()?,
96				value: input.parse()?,
97			})
98		} else if lookahead.peek(kw::gen) {
99			Ok(OrchestraAttrItem::MessageWrapperName {
100				tag: input.parse::<kw::gen>()?,
101				eq_token: input.parse()?,
102				value: input.parse()?,
103			})
104		} else if lookahead.peek(kw::signal_capacity) {
105			Ok(OrchestraAttrItem::SignalChannelCapacity {
106				tag: input.parse::<kw::signal_capacity>()?,
107				eq_token: input.parse()?,
108				value: input.parse::<LitInt>()?.base10_parse::<usize>()?,
109			})
110		} else if lookahead.peek(kw::message_capacity) {
111			Ok(OrchestraAttrItem::MessageChannelCapacity {
112				tag: input.parse::<kw::message_capacity>()?,
113				eq_token: input.parse()?,
114				value: input.parse::<LitInt>()?.base10_parse::<usize>()?,
115			})
116		} else if lookahead.peek(kw::boxed_messages) {
117			Ok(OrchestraAttrItem::BoxedMessages {
118				tag: input.parse::<kw::boxed_messages>()?,
119				eq_token: input.parse()?,
120				value: input.parse::<LitBool>()?.value(),
121			})
122		} else {
123			Err(lookahead.error())
124		}
125	}
126}
127
128/// Attribute arguments
129#[derive(Clone, Debug)]
130pub(crate) struct OrchestraAttrArgs {
131	pub(crate) message_wrapper: Ident,
132	pub(crate) extern_event_ty: Path,
133	pub(crate) extern_signal_ty: Path,
134	pub(crate) extern_error_ty: Path,
135	pub(crate) outgoing_ty: Option<Path>,
136	pub(crate) signal_channel_capacity: usize,
137	pub(crate) message_channel_capacity: usize,
138	pub(crate) boxed_messages: bool,
139}
140
141macro_rules! extract_variant {
142	($unique:expr, $variant:ident ; default = $fallback:expr) => {
143		extract_variant!($unique, $variant).unwrap_or_else(|| $fallback)
144	};
145	($unique:expr, $variant:ident ; err = $err:expr) => {
146		extract_variant!($unique, $variant).ok_or_else(|| Error::new(Span::call_site(), $err))
147	};
148	($unique:expr, $variant:ident) => {
149		$unique.values().find_map(|item| {
150			if let OrchestraAttrItem::$variant { value, .. } = item {
151				Some(value.clone())
152			} else {
153				None
154			}
155		})
156	};
157}
158
159impl Parse for OrchestraAttrArgs {
160	fn parse(input: &ParseBuffer) -> Result<Self> {
161		let items: Punctuated<OrchestraAttrItem, Token![,]> =
162			input.parse_terminated(OrchestraAttrItem::parse)?;
163
164		let mut unique = HashMap::<
165			std::mem::Discriminant<OrchestraAttrItem>,
166			OrchestraAttrItem,
167			RandomState,
168		>::default();
169		for item in items {
170			if let Some(first) = unique.insert(std::mem::discriminant(&item), item.clone()) {
171				let mut e = Error::new(
172					item.span(),
173					format!("Duplicate definition of orchestra generation type found"),
174				);
175				e.combine(Error::new(first.span(), "previously defined here."));
176				return Err(e)
177			}
178		}
179
180		let signal_channel_capacity =
181			extract_variant!(unique, SignalChannelCapacity; default = 64_usize);
182		let message_channel_capacity =
183			extract_variant!(unique, MessageChannelCapacity; default = 1024_usize);
184
185		let error = extract_variant!(unique, ExternErrorType; err = "Must declare the orchestra error type via `error=..`.")?;
186		let event = extract_variant!(unique, ExternEventType; err = "Must declare the orchestra event type via `event=..`.")?;
187		let signal = extract_variant!(unique, ExternOrchestraSignalType; err = "Must declare the orchestra signal type via `signal=..`.")?;
188		let message_wrapper = extract_variant!(unique, MessageWrapperName; err = "Must declare the orchestra generated wrapping message type via `gen=..`.")?;
189		let outgoing = extract_variant!(unique, OutgoingType);
190		let boxed_messages = extract_variant!(unique, BoxedMessages; default = false);
191
192		Ok(OrchestraAttrArgs {
193			signal_channel_capacity,
194			message_channel_capacity,
195			extern_event_ty: event,
196			extern_signal_ty: signal,
197			extern_error_ty: error,
198			outgoing_ty: outgoing,
199			message_wrapper,
200			boxed_messages,
201		})
202	}
203}