use crate::ast::{DataType, Expr, Extension, Query, Statement};
use crate::dialect::FeatureSet;
use crate::precedence::BindingPower;
use crate::vocab::{NodeId, Resolver, Span, Symbol};
use std::fmt;
mod dyn_ext;
mod nodes;
pub use dyn_ext::{DynAstExt, DynExt};
pub use nodes::{render_extension_infix, render_extension_prefix};
#[cfg(test)]
mod tests;
pub trait Render {
fn render(&self, ctx: &RenderCtx<'_>, f: &mut fmt::Formatter<'_>) -> fmt::Result;
fn operand_binding_power(&self) -> Option<BindingPower> {
None
}
}
pub type RenderResult<T> = Result<T, RenderError>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RenderError {
kind: RenderErrorKind,
span: Option<Span>,
message: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RenderErrorKind {
Unsupported,
Format,
}
impl RenderError {
pub fn unsupported(span: Option<Span>, message: impl Into<String>) -> Self {
Self {
kind: RenderErrorKind::Unsupported,
span,
message: message.into(),
}
}
pub fn kind(&self) -> RenderErrorKind {
self.kind
}
pub fn span(&self) -> Option<Span> {
self.span
}
pub fn message(&self) -> &str {
&self.message
}
}
impl fmt::Display for RenderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.span {
Some(span) if !span.is_synthetic() => {
write!(
f,
"{} at bytes {}..{}",
self.message,
span.start(),
span.end()
)
}
_ => f.write_str(&self.message),
}
}
}
impl std::error::Error for RenderError {}
impl From<fmt::Error> for RenderError {
fn from(_error: fmt::Error) -> Self {
Self {
kind: RenderErrorKind::Format,
span: None,
message: "render formatter failed".to_owned(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum RenderMode {
#[default]
Canonical,
Parenthesized,
Redacted,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum RenderSpelling {
#[default]
PreserveSource,
TargetDialect,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RenderConfig {
pub mode: RenderMode,
pub target: FeatureSet,
pub spelling: RenderSpelling,
}
impl RenderConfig {
const DEFAULT: RenderConfig = RenderConfig {
mode: RenderMode::Canonical,
target: FeatureSet::ANSI,
spelling: RenderSpelling::PreserveSource,
};
}
impl Default for RenderConfig {
fn default() -> Self {
Self::DEFAULT
}
}
const UNRESOLVED_SYMBOL: &str = "<unresolved>";
#[derive(Clone, Copy, PartialEq, Eq)]
enum ContextKind {
Canonical,
Debug,
}
pub struct RenderCtx<'a> {
resolver: &'a dyn Resolver,
source: &'a str,
config: &'a RenderConfig,
kind: ContextKind,
}
impl<'a> RenderCtx<'a> {
pub fn new(resolver: &'a dyn Resolver, source: &'a str, config: &'a RenderConfig) -> Self {
Self {
resolver,
source,
config,
kind: ContextKind::Canonical,
}
}
pub fn debug(resolver: &'a dyn Resolver, config: &'a RenderConfig) -> Self {
Self {
resolver,
source: "",
config,
kind: ContextKind::Debug,
}
}
pub fn resolver(&self) -> &dyn Resolver {
self.resolver
}
pub fn source(&self) -> &str {
self.source
}
pub fn config(&self) -> &RenderConfig {
self.config
}
pub fn mode(&self) -> RenderMode {
self.config.mode
}
pub fn target(&self) -> &FeatureSet {
&self.config.target
}
pub fn spelling(&self) -> RenderSpelling {
self.config.spelling
}
fn resolve(&self, sym: Symbol) -> &str {
match self.kind {
ContextKind::Canonical => self.resolver.resolve(sym),
ContextKind::Debug => self.resolver.try_resolve(sym).unwrap_or(UNRESOLVED_SYMBOL),
}
}
fn slice(&self, span: Span) -> Option<&str> {
if self.kind == ContextKind::Debug || span.is_synthetic() {
return None;
}
self.source.get(span.start() as usize..span.end() as usize)
}
}
pub struct Displayed<'a, T: Render>(&'a T, &'a RenderCtx<'a>);
impl<T: Render> fmt::Display for Displayed<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.render(self.1, f)
}
}
pub struct DebugSql<'a, T: Render> {
node: &'a T,
ctx: RenderCtx<'a>,
}
impl<T: Render> fmt::Display for DebugSql<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.node.render(&self.ctx, f)
}
}
pub trait RenderExt: Render {
fn displayed<'a>(&'a self, ctx: &'a RenderCtx<'a>) -> Displayed<'a, Self>
where
Self: Sized;
fn debug_sql<'a>(&'a self, resolver: &'a dyn Resolver) -> DebugSql<'a, Self>
where
Self: Sized,
{
DebugSql {
node: self,
ctx: RenderCtx::debug(resolver, &RenderConfig::DEFAULT),
}
}
}
impl<T: Render> RenderExt for T {
fn displayed<'a>(&'a self, ctx: &'a RenderCtx<'a>) -> Displayed<'a, Self> {
Displayed(self, ctx)
}
}
pub trait FragmentRender: Render + fragment_sealed::Sealed {
#[doc(hidden)]
fn fragment_node_id(&self) -> NodeId;
}
mod fragment_sealed {
pub trait Sealed {}
}
impl<X: Extension + Render> fragment_sealed::Sealed for Query<X> {}
impl<X: Extension + Render> FragmentRender for Query<X> {
fn fragment_node_id(&self) -> NodeId {
self.meta.node_id
}
}
impl<X: Extension + Render> fragment_sealed::Sealed for Statement<X> {}
impl<X: Extension + Render> FragmentRender for Statement<X> {
fn fragment_node_id(&self) -> NodeId {
match self {
Statement::Query { meta, .. }
| Statement::CreateTable { meta, .. }
| Statement::AlterTable { meta, .. }
| Statement::Drop { meta, .. }
| Statement::CreateSchema { meta, .. }
| Statement::CreateView { meta, .. }
| Statement::AlterView { meta, .. }
| Statement::CreateIndex { meta, .. }
| Statement::CreateFunction { meta, .. }
| Statement::CreateProcedure { meta, .. }
| Statement::AlterRoutine { meta, .. }
| Statement::CreateEvent { meta, .. }
| Statement::AlterEvent { meta, .. }
| Statement::DropEvent { meta, .. }
| Statement::DropDatabase { meta, .. }
| Statement::DropIndex { meta, .. }
| Statement::CreateDatabase { meta, .. }
| Statement::DropRoutine { meta, .. }
| Statement::DropTransform { meta, .. }
| Statement::Truncate { meta, .. }
| Statement::CommentOn { meta, .. }
| Statement::Insert { meta, .. }
| Statement::Update { meta, .. }
| Statement::Delete { meta, .. }
| Statement::Merge { meta, .. }
| Statement::Transaction { meta, .. }
| Statement::Xa { meta, .. }
| Statement::Session { meta, .. }
| Statement::AccessControl { meta, .. }
| Statement::Copy { meta, .. }
| Statement::CopyInto { meta, .. }
| Statement::Export { meta, .. }
| Statement::Import { meta, .. }
| Statement::Explain { meta, .. }
| Statement::Describe { meta, .. }
| Statement::Show { meta, .. }
| Statement::Kill { meta, .. }
| Statement::Handler { meta, .. }
| Statement::Install { meta, .. }
| Statement::Uninstall { meta, .. }
| Statement::Shutdown { meta, .. }
| Statement::Restart { meta, .. }
| Statement::Clone { meta, .. }
| Statement::ImportTable { meta, .. }
| Statement::Help { meta, .. }
| Statement::Binlog { meta, .. }
| Statement::Pragma { meta, .. }
| Statement::Attach { meta, .. }
| Statement::Detach { meta, .. }
| Statement::Checkpoint { meta, .. }
| Statement::Load { meta, .. }
| Statement::LoadData { meta, .. }
| Statement::UpdateExtensions { meta, .. }
| Statement::Vacuum { meta, .. }
| Statement::Reindex { meta, .. }
| Statement::Analyze { meta, .. }
| Statement::Use { meta, .. }
| Statement::CreateTrigger { meta, .. }
| Statement::CreateStoredTrigger { meta, .. }
| Statement::CreateMacro { meta, .. }
| Statement::CreateSecret { meta, .. }
| Statement::DropSecret { meta, .. }
| Statement::CreateType { meta, .. }
| Statement::CreateVirtualTable { meta, .. }
| Statement::CreateSequence { meta, .. }
| Statement::CreateExtension { meta, .. }
| Statement::AlterExtension { meta, .. }
| Statement::CreateTablespace { meta, .. }
| Statement::AlterTablespace { meta, .. }
| Statement::DropTablespace { meta, .. }
| Statement::CreateLogfileGroup { meta, .. }
| Statement::AlterLogfileGroup { meta, .. }
| Statement::DropLogfileGroup { meta, .. }
| Statement::AlterObjectDepends { meta, .. }
| Statement::AlterSystem { meta, .. }
| Statement::AlterDatabase { meta, .. }
| Statement::AlterDatabaseOptions { meta, .. }
| Statement::CreateServer { meta, .. }
| Statement::AlterServer { meta, .. }
| Statement::DropServer { meta, .. }
| Statement::AlterInstance { meta, .. }
| Statement::CreateSpatialReferenceSystem { meta, .. }
| Statement::DropSpatialReferenceSystem { meta, .. }
| Statement::CreateResourceGroup { meta, .. }
| Statement::AlterResourceGroup { meta, .. }
| Statement::DropResourceGroup { meta, .. }
| Statement::AlterSequence { meta, .. }
| Statement::AlterObjectSchema { meta, .. }
| Statement::Pivot { meta, .. }
| Statement::Unpivot { meta, .. }
| Statement::ShowRef { meta, .. }
| Statement::Prepare { meta, .. }
| Statement::Execute { meta, .. }
| Statement::PrepareFrom { meta, .. }
| Statement::ExecuteUsing { meta, .. }
| Statement::Deallocate { meta, .. }
| Statement::Call { meta, .. }
| Statement::Do { meta, .. }
| Statement::DoExpressions { meta, .. }
| Statement::LockTables { meta, .. }
| Statement::UnlockTables { meta, .. }
| Statement::InstanceLock { meta, .. }
| Statement::Compound { meta, .. }
| Statement::If { meta, .. }
| Statement::Case { meta, .. }
| Statement::Loop { meta, .. }
| Statement::While { meta, .. }
| Statement::Repeat { meta, .. }
| Statement::Leave { meta, .. }
| Statement::Iterate { meta, .. }
| Statement::Return { meta, .. }
| Statement::OpenCursor { meta, .. }
| Statement::FetchCursor { meta, .. }
| Statement::CloseCursor { meta, .. }
| Statement::TableMaintenance { meta, .. }
| Statement::CacheIndex { meta, .. }
| Statement::LoadIndex { meta, .. }
| Statement::Rename { meta, .. }
| Statement::Flush { meta, .. }
| Statement::Purge { meta, .. }
| Statement::Replication { meta, .. }
| Statement::CreateUser { meta, .. }
| Statement::AlterUser { meta, .. }
| Statement::UserRoleList { meta, .. }
| Statement::Signal { meta, .. }
| Statement::Resignal { meta, .. }
| Statement::GetDiagnostics { meta, .. }
| Statement::Other { meta, .. } => meta.node_id,
}
}
}
impl<X: Extension + Render> fragment_sealed::Sealed for Expr<X> {}
impl<X: Extension + Render> FragmentRender for Expr<X> {
fn fragment_node_id(&self) -> NodeId {
match self {
Expr::Column { meta, .. }
| Expr::Literal { meta, .. }
| Expr::BinaryOp { meta, .. }
| Expr::UnaryOp { meta, .. }
| Expr::Function { meta, .. }
| Expr::Case { meta, .. }
| Expr::Extract { meta, .. }
| Expr::Cast { meta, .. }
| Expr::IsNull { meta, .. }
| Expr::IsTruth { meta, .. }
| Expr::IsNormalized { meta, .. }
| Expr::Between { meta, .. }
| Expr::Like { meta, .. }
| Expr::InList { meta, .. }
| Expr::InSubquery { meta, .. }
| Expr::InExpr { meta, .. }
| Expr::Exists { meta, .. }
| Expr::QuantifiedComparison { meta, .. }
| Expr::QuantifiedList { meta, .. }
| Expr::QuantifiedLike { meta, .. }
| Expr::Subquery { meta, .. }
| Expr::Parameter { meta, .. }
| Expr::PositionalColumn { meta, .. }
| Expr::SessionVariable { meta, .. }
| Expr::Subscript { meta, .. }
| Expr::SemiStructuredAccess { meta, .. }
| Expr::Collate { meta, .. }
| Expr::AtTimeZone { meta, .. }
| Expr::Interval { meta, .. }
| Expr::Array { meta, .. }
| Expr::Struct { meta, .. }
| Expr::StructConstructor { meta, .. }
| Expr::Map { meta, .. }
| Expr::Row { meta, .. }
| Expr::FieldSelection { meta, .. }
| Expr::NamedOperator { meta, .. }
| Expr::PrefixOperator { meta, .. }
| Expr::PostfixOperator { meta, .. }
| Expr::Lambda { meta, .. }
| Expr::Columns { meta, .. }
| Expr::SpecialFunction { meta, .. }
| Expr::JsonFunc { meta, .. }
| Expr::JsonObject { meta, .. }
| Expr::JsonArray { meta, .. }
| Expr::JsonAggregate { meta, .. }
| Expr::JsonConstructor { meta, .. }
| Expr::IsJson { meta, .. }
| Expr::XmlFunc { meta, .. }
| Expr::IsDocument { meta, .. }
| Expr::StringFunc { meta, .. }
| Expr::Other { meta, .. } => meta.node_id,
}
}
}
impl<X: Extension + Render> fragment_sealed::Sealed for DataType<X> {}
impl<X: Extension + Render> FragmentRender for DataType<X> {
fn fragment_node_id(&self) -> NodeId {
match self {
DataType::Boolean { meta, .. }
| DataType::TinyInt { meta, .. }
| DataType::SmallInt { meta, .. }
| DataType::MediumInt { meta, .. }
| DataType::Integer { meta, .. }
| DataType::BigInt { meta, .. }
| DataType::Decimal { meta, .. }
| DataType::Float { meta, .. }
| DataType::Real { meta, .. }
| DataType::Double { meta, .. }
| DataType::Text { meta, .. }
| DataType::Blob { meta, .. }
| DataType::Character { meta, .. }
| DataType::Binary { meta, .. }
| DataType::Bit { meta, .. }
| DataType::Json { meta, .. }
| DataType::Uuid { meta, .. }
| DataType::Date { meta, .. }
| DataType::Time { meta, .. }
| DataType::Timestamp { meta, .. }
| DataType::Interval { meta, .. }
| DataType::Enum { meta, .. }
| DataType::Set { meta, .. }
| DataType::NumericModifier { meta, .. }
| DataType::Array { meta, .. }
| DataType::Struct { meta, .. }
| DataType::Union { meta, .. }
| DataType::Map { meta, .. }
| DataType::Wrapped { meta, .. }
| DataType::FixedString { meta, .. }
| DataType::DateTime64 { meta, .. }
| DataType::Nested { meta, .. }
| DataType::FixedWidthInt { meta, .. }
| DataType::UserDefined { meta, .. }
| DataType::Liberal { meta, .. }
| DataType::Other { meta, .. } => meta.node_id,
}
}
}