mod procedure;
mod scalar;
mod table;
pub use procedure::render_procedure_as;
pub use scalar::render_scalar_function_as;
pub use table::render_table_function_as;
use crate::routine_spec::{RoutineParameter, RoutineSpec};
pub(crate) fn plain_name(routine: &RoutineSpec) -> String {
routine.qualified_name(std::borrow::ToOwned::to_owned)
}
pub(crate) fn quoted_name(routine: &RoutineSpec, open: char, close: char) -> String {
routine.qualified_name(|part| format!("{open}{part}{close}"))
}
pub(crate) fn quoted_text(text: &str) -> String {
text.replace('\'', "''")
}
pub(crate) fn or_replace(routine: &RoutineSpec) -> &'static str {
if routine.or_replace { "OR REPLACE " } else { "" }
}
pub(crate) fn name_and_type(parameter: &RoutineParameter) -> String {
if parameter.name.is_empty() {
parameter.data_type.clone()
} else {
format!("{} {}", parameter.name, parameter.data_type)
}
}
pub(crate) fn inline_parameters(routine: &RoutineSpec) -> String {
routine.parameters.iter().map(name_and_type).collect::<Vec<_>>().join(", ")
}
pub(crate) fn parameter_types(routine: &RoutineSpec) -> String {
routine.parameters.iter().map(|p| p.data_type.clone()).collect::<Vec<_>>().join(", ")
}
pub(crate) fn indented(statement: &str, pad: &str) -> String {
format!("{pad}{statement}")
}
pub(crate) fn terminated_statements(
statements: &[String],
pad: &str,
terminator: &str,
) -> Vec<String> {
statements
.iter()
.map(|statement| {
let statement = indented(statement, pad);
if statement.trim_end().ends_with(terminator) {
statement
} else {
format!("{statement}{terminator}")
}
})
.collect()
}
pub(crate) fn refusal_comment(kind: &str, name: &str, orientation: &str) -> String {
crate::comment::block_comment(&format!(
"No {kind} named `{name}` is written here.\n\
{orientation}"
))
}
pub(crate) fn note_above(note: &str, rendered: &str) -> String {
format!("{said_1}\n{rendered}", said_1 = crate::comment::block_comment(note))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::routine_spec::{RoutineKind, RoutineSpec};
fn a_spec() -> RoutineSpec {
let mut spec = RoutineSpec::new(RoutineKind::Procedure, "load_customer");
spec.schema = Some("retail_core".to_owned());
spec
}
#[test]
fn a_name_is_quoted_the_way_the_grammar_asked() {
assert_eq!(plain_name(&a_spec()), "retail_core.load_customer");
assert_eq!(quoted_name(&a_spec(), '[', ']'), "[retail_core].[load_customer]");
assert_eq!(quoted_name(&a_spec(), '`', '`'), "`retail_core`.`load_customer`");
}
#[test]
fn a_quote_inside_a_description_is_doubled() {
assert_eq!(quoted_text("the day's rows"), "the day''s rows");
}
#[test]
fn a_statement_is_terminated_once_and_not_twice() {
let statements = vec!["DELETE FROM stage".to_owned(), "IF x THEN NULL; END IF;".to_owned()];
assert_eq!(
terminated_statements(&statements, " ", ";"),
vec![" DELETE FROM stage;".to_owned(), " IF x THEN NULL; END IF;".to_owned()]
);
}
}