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::*;
pub use delete::{visit_delete, visit_using};
pub use insert::visit_insert;
pub use select::{
visit_select_chain, visit_select_combinator, visit_select_command, visit_select_item,
};
pub use update::visit_update;
use crate::{
clause::{Fields, FromChain, With},
correlations::CorrelationId,
keyword,
parse_option::ParseOption,
part::TargetTable,
pretty::{PrettyPrint, Printer},
quote_option::QuoteOption,
scopes::{ScopeId, Scoped},
visit::Visit,
};
pub struct Command {
pub scope_id: ScopeId,
pub attrs: Vec<Attribute>,
pub with: Option<With>,
pub command_type: CommandType,
pub correlation_id: CorrelationId,
}
impl Command {
#[must_use]
pub fn fields(&self) -> Option<&Fields> {
self.command_type.fields()
}
}
pub fn visit_command<'a>(visit: &mut (impl Visit<'a> + ?Sized), command: &'a Command) {
if let Some(inner) = &command.with {
visit.visit_with(inner);
}
visit.visit_command_type(&command.command_type);
}
impl Scoped for Command {
#[inline]
fn scope_id(&self) -> ScopeId {
self.scope_id
}
#[inline]
fn with(&self) -> Option<&With> {
self.with.as_ref()
}
#[inline]
fn target_table(&self) -> Option<&TargetTable> {
self.command_type.target_table()
}
#[inline]
fn select_chain(&self) -> Option<&SelectChain> {
match &self.command_type {
CommandType::Select(select) => Some(&select.chain),
_ => None,
}
}
#[inline]
#[allow(clippy::wrong_self_convention)]
fn from_chain(&self) -> Option<&FromChain> {
self.command_type.from_chain()
}
}
impl Parse for Command {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
scope_id: ScopeId::new(),
attrs: input.call(Attribute::parse_outer)?,
with: input.call(With::parse_option)?,
command_type: input.parse()?,
correlation_id: CorrelationId::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);
});
}
}
impl PrettyPrint for Command {
fn pretty_print(&self, printer: &mut Printer<'_>) {
self.attrs.pretty_print(printer);
self.with.pretty_print(printer);
self.command_type.pretty_print(printer);
}
}
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)
}
#[must_use]
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.fields()),
Self::Update(inner) => inner.returning.as_ref().map(|returning| &returning.fields),
}
}
#[must_use]
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),
}
}
#[must_use]
#[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) => {
if select.chain.combinators.is_empty() {
match &select.chain.start {
SelectItem::Paren { .. } => None,
SelectItem::Core(core) => core.from_chain(),
}
} else {
None
}
}
Self::Update(update) => update.from.as_ref().map(|from| &from.chain),
}
}
}
pub fn visit_command_type<'a>(
visit: &mut (impl Visit<'a> + ?Sized),
command_type: &'a CommandType,
) {
match command_type {
CommandType::Delete(inner) => visit.visit_delete(inner),
CommandType::Insert(inner) => visit.visit_insert(inner),
CommandType::Select(inner) => visit.visit_select_command(inner),
CommandType::Update(inner) => visit.visit_update(inner),
}
}
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);
}
}
impl PrettyPrint for CommandType {
fn pretty_print(&self, printer: &mut Printer<'_>) {
match self {
Self::Delete(inner) => inner.pretty_print(printer),
Self::Insert(inner) => inner.pretty_print(printer),
Self::Select(inner) => inner.pretty_print(printer),
Self::Update(inner) => inner.pretty_print(printer),
}
}
}