use crate::queries::spec::QuerySpec;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub fn of<'a>(&self, source: &'a str) -> &'a str {
&source[self.start..self.end]
}
pub fn is_empty(&self) -> bool {
self.end <= self.start
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Nesting {
Flat,
ToOne(String),
ToMany(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OutputColumn {
pub name: String,
pub field: String,
pub nesting: Nesting,
pub rust_type: String,
pub nullable: bool,
pub outer_join: bool,
pub inner_nullable: bool,
pub rule: &'static str,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Param {
pub number: usize,
pub name: String,
pub rust_type: String,
pub rule: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Placeholder {
pub span: Span,
pub number: usize,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Clauses {
pub select_list: Option<Span>,
pub from: Option<Span>,
pub where_: Option<Span>,
pub group_by: Option<Span>,
pub having: Option<Span>,
pub order_by: Option<Span>,
pub limit: Option<Span>,
pub offset: Option<Span>,
pub unsupported: Option<String>,
}
impl Clauses {
pub fn present(&self) -> Vec<(&'static str, Span)> {
let mut out = Vec::new();
for (name, span) in [
("from", self.from),
("where", self.where_),
("group_by", self.group_by),
("having", self.having),
("order_by", self.order_by),
("limit", self.limit),
("offset", self.offset),
] {
if let Some(s) = span {
out.push((name, s));
}
}
out
}
}
#[derive(Debug, Clone)]
pub struct Analysis {
pub spec: QuerySpec,
pub outputs: Vec<OutputColumn>,
pub params: Vec<Param>,
pub placeholders: Vec<Placeholder>,
pub clauses: Clauses,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Part {
Sql(Span),
Arg(usize),
}
impl Analysis {
pub fn parts(&self, span: Span) -> Vec<Part> {
let mut parts = Vec::new();
let mut at = span.start;
for ph in &self.placeholders {
if ph.span.start < span.start || ph.span.end > span.end {
continue;
}
if ph.span.start > at {
parts.push(Part::Sql(Span {
start: at,
end: ph.span.start,
}));
}
let index = self
.params
.iter()
.position(|p| p.number == ph.number)
.expect("every placeholder has a param (checked during analysis)");
parts.push(Part::Arg(index));
at = ph.span.end;
}
if at < span.end {
parts.push(Part::Sql(Span {
start: at,
end: span.end,
}));
}
parts
}
pub fn statement_parts(&self) -> Vec<Part> {
self.parts(Span {
start: self.spec.sql_start,
end: self.spec.sql_end,
})
}
}