use std::collections::HashSet;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
bracketed,
parse::{Parse, ParseBuffer, ParseStream},
token::Bracket,
Generics, Ident, Token,
};
use crate::client::{ClientKind, SharedClient};
#[derive(Debug, Clone)]
pub struct Output {
pub name: Ident,
pub generics: Option<Generics>,
pub options: Option<Vec<Ident>>,
pub rate_transition: Option<SharedClient>,
pub extras: Option<Vec<ClientKind>>,
pub logging: bool,
}
impl Output {
pub fn new(name: Ident, generics: Option<Generics>) -> Self {
Self {
name,
generics,
options: None,
rate_transition: None,
extras: None,
logging: false,
}
}
pub fn expand_name(&self) -> TokenStream {
let name = &self.name;
if let Some(generics) = self.generics.as_ref() {
quote!(#name #generics)
} else {
quote!(#name)
}
}
pub fn collect(&self, clients: &mut HashSet<SharedClient>) {
self.rate_transition
.as_ref()
.map(|client| clients.insert(client.clone()));
}
pub fn add_rate_transition(&mut self, actor: Ident, output_rate: usize, input_rate: usize) {
self.rate_transition = Some(SharedClient::sampler(
actor,
self.name.clone(),
output_rate,
input_rate,
));
}
pub fn add_option(&mut self, option: &str) {
self.options
.get_or_insert(vec![])
.push(Ident::new(option, Span::call_site()));
}
pub fn add_logging(&mut self) {
self.logging = true;
}
}
impl<'a> TryFrom<ParseBuffer<'a>> for Output {
type Error = syn::parse::Error;
fn try_from(content: ParseBuffer<'a>) -> Result<Self, Self::Error> {
Ok(Output::new(
content.parse::<Ident>()?,
content.parse::<Generics>().ok(),
))
}
}
impl Parse for Output {
fn parse(input: ParseStream) -> syn::Result<Self> {
input
.peek(Bracket)
.then(|| {
let content;
let _ = bracketed!(content in input);
let mut output = Output::try_from(content)?;
loop {
match (
input.peek(Token![!]),
input.peek(Token![$]),
input.peek(Token![..]),
) {
(true, false, false) => {
input
.parse::<Token![!]>()
.map(|_| output.add_option("bootstrap"))?;
}
(false, false, true) => {
input
.parse::<Token![..]>()
.map(|_| output.add_option("unbounded"))?;
}
(false, true, false) => {
input.parse::<Token![$]>().map(|_| output.add_logging())?;
}
(false, false, false) => break,
_ => unimplemented!(),
}
}
Ok(output)
})
.ok_or(syn::Error::new(input.span(), "no output given "))
.and_then(|maybe_output| maybe_output)
}
}