use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{
Ident, Token, parenthesized,
parse::{Parse, ParseStream},
punctuated::Punctuated,
};
use crate::{
command::{Command, CommandType},
correlations::CorrelationId,
keyword,
part::TableAlias,
visitor::Visitor,
};
pub struct With {
pub _with_keyword: keyword::with,
pub items: Punctuated<WithItem, Token![,]>,
}
impl With {
pub fn parse_optional(input: ParseStream) -> syn::Result<Option<Self>> {
Self::peek(input).then(|| input.parse()).transpose()
}
pub fn peek(input: ParseStream) -> bool {
input.peek(keyword::with)
}
pub fn accept<'a>(&'a self, visitor: &mut impl Visitor<'a>) {
for item in &self.items {
item.accept(visitor);
}
}
}
impl Parse for With {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
_with_keyword: input.call(keyword::with::parse_autocomplete)?,
items: {
let mut punctuated = Punctuated::new();
while !input.is_empty() {
if CommandType::peek(input) {
break;
}
punctuated.push(input.parse()?);
if CommandType::peek(input) {
break;
}
punctuated.push_punct(input.parse()?);
}
if punctuated.is_empty() {
return Err(syn::Error::new(input.span(), "with clause cannot be empty"));
}
punctuated
},
})
}
}
impl ToTokens for With {
fn to_tokens(&self, tokens: &mut TokenStream) {
let items = self.items.iter();
quote! { ::kosame::repr::clause::With::new(&[#(#items),*]) }.to_tokens(tokens)
}
}
pub struct WithItem {
pub alias: TableAlias,
pub _as_token: Token![as],
pub _paren_token: syn::token::Paren,
pub command: Command,
pub correlation_id: CorrelationId,
}
impl WithItem {
pub fn accept<'a>(&'a self, visitor: &mut impl Visitor<'a>) {
self.command.accept(visitor);
}
pub fn columns(&self) -> Vec<&Ident> {
match &self.alias.columns {
Some(columns) => columns.columns.iter().collect(),
None => self
.command
.fields()
.into_iter()
.flat_map(|fields| fields.columns())
.collect(),
}
}
}
impl Parse for WithItem {
fn parse(input: ParseStream) -> syn::Result<Self> {
let content;
Ok(Self {
alias: input.parse()?,
_as_token: input.parse()?,
_paren_token: parenthesized!(content in input),
command: content.parse()?,
correlation_id: CorrelationId::new(),
})
}
}
impl ToTokens for WithItem {
fn to_tokens(&self, tokens: &mut TokenStream) {
let alias = &self.alias;
let command = &self.command;
quote! { ::kosame::repr::clause::WithItem::new(#alias, #command) }.to_tokens(tokens);
}
}