mod delete;
mod insert;
mod select;
mod update;
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{
Attribute,
parse::{Parse, ParseStream},
};
pub use delete::*;
pub use insert::*;
pub use select::*;
pub use update::*;
use crate::{
clause::{Fields, FromChain, With},
correlations::CorrelationId,
keyword,
part::TargetTable,
quote_option::QuoteOption,
scopes::ScopeId,
visitor::Visitor,
};
pub struct Command {
pub attrs: Vec<Attribute>,
pub with: Option<With>,
pub command_type: CommandType,
pub correlation_id: CorrelationId,
pub scope_id: ScopeId,
}
impl Command {
pub fn accept<'a>(&'a self, visitor: &mut impl Visitor<'a>) {
visitor.visit_command(self);
{
if let Some(inner) = &self.with {
inner.accept(visitor)
}
self.command_type.accept(visitor);
}
}
pub fn fields(&self) -> Option<&Fields> {
self.command_type.fields()
}
pub fn target_table(&self) -> Option<&TargetTable> {
self.command_type.target_table()
}
#[allow(clippy::wrong_self_convention)]
pub fn from_chain(&self) -> Option<&FromChain> {
self.command_type.from_chain()
}
}
impl Parse for Command {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
attrs: input.call(Attribute::parse_outer)?,
with: input.call(With::parse_optional)?,
command_type: input.parse()?,
correlation_id: CorrelationId::new(),
scope_id: ScopeId::new(),
})
}
}
impl ToTokens for Command {
fn to_tokens(&self, tokens: &mut TokenStream) {
let with = QuoteOption::from(&self.with);
let command_type = &self.command_type;
self.scope_id.scope(|| {
quote! { ::kosame::repr::command::Command::new(#with, #command_type) }
.to_tokens(tokens);
});
}
}
pub enum CommandType {
Delete(Delete),
Insert(Insert),
Select(Box<Select>),
Update(Update),
}
impl CommandType {
pub fn peek(input: ParseStream) -> bool {
Delete::peek(input) || Insert::peek(input) || Select::peek(input) || Update::peek(input)
}
pub fn accept<'a>(&'a self, visitor: &mut impl Visitor<'a>) {
match self {
Self::Delete(inner) => inner.accept(visitor),
Self::Insert(inner) => inner.accept(visitor),
Self::Select(inner) => inner.accept(visitor),
Self::Update(inner) => inner.accept(visitor),
}
}
pub fn fields(&self) -> Option<&Fields> {
match self {
Self::Delete(inner) => inner.returning.as_ref().map(|returning| &returning.fields),
Self::Insert(inner) => inner.returning.as_ref().map(|returning| &returning.fields),
Self::Select(inner) => Some(&inner.select.fields),
Self::Update(inner) => inner.returning.as_ref().map(|returning| &returning.fields),
}
}
pub fn target_table(&self) -> Option<&TargetTable> {
match self {
Self::Delete(delete) => Some(&delete.target_table),
Self::Insert(insert) => Some(&insert.target_table),
Self::Select(..) => None,
Self::Update(update) => Some(&update.target_table),
}
}
#[allow(clippy::wrong_self_convention)]
pub fn from_chain(&self) -> Option<&FromChain> {
match self {
Self::Delete(delete) => delete.using.as_ref().map(|using| &using.chain),
Self::Insert(..) => None,
Self::Select(select) => select.from.as_ref().map(|from| &from.chain),
Self::Update(update) => update.from.as_ref().map(|from| &from.chain),
}
}
}
impl Parse for CommandType {
fn parse(input: ParseStream) -> syn::Result<Self> {
if Delete::peek(input) {
Ok(Self::Delete(input.parse()?))
} else if Insert::peek(input) {
Ok(Self::Insert(input.parse()?))
} else if Select::peek(input) {
Ok(Self::Select(input.parse()?))
} else if Update::peek(input) {
Ok(Self::Update(input.parse()?))
} else {
keyword::group_command::error(input);
}
}
}
impl ToTokens for CommandType {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Delete(inner) => quote! {
::kosame::repr::command::CommandType::Delete(#inner)
},
Self::Insert(inner) => quote! {
::kosame::repr::command::CommandType::Insert(#inner)
},
Self::Select(inner) => quote! {
::kosame::repr::command::CommandType::Select(#inner)
},
Self::Update(inner) => quote! {
::kosame::repr::command::CommandType::Update(#inner)
},
}
.to_tokens(tokens)
}
}