#[must_use]
pub fn escape_identifier(s: &str) -> String {
format!("\"{}\"", s.replace('"', "\"\""))
}
#[must_use]
pub fn escape_sql_string(s: &str) -> String {
format!("'{}'", s.replace('\'', "''"))
}
#[must_use]
pub fn epsg_crs(srid: i32) -> String {
escape_sql_string(&format!("EPSG:{srid}"))
}
#[must_use]
pub fn escape_relation(relation: &str) -> String {
relation
.split('.')
.map(escape_identifier)
.collect::<Vec<_>>()
.join(".")
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
#[case::simple("roads", "\"roads\"")]
#[case::embedded_quote("my\"table", "\"my\"\"table\"")]
#[case::empty("", "\"\"")]
#[case::quote_only("\"", "\"\"\"\"")]
#[case::multiple_quotes("a\"\"b", "\"a\"\"\"\"b\"")]
#[case::dot_in_identifier("schema.table", "\"schema.table\"")]
#[case::unicode("Straße", "\"Straße\"")]
fn escape_identifier_cases(#[case] input: &str, #[case] expected: &str) {
assert_eq!(escape_identifier(input), expected);
}
#[rstest]
#[case::simple("simple", "'simple'")]
#[case::embedded_apostrophe("O'Brien", "'O''Brien'")]
#[case::empty("", "''")]
#[case::apostrophe_only("'", "''''")]
#[case::multiple_apostrophes("a''b", "'a''''b'")]
#[case::double_quotes_preserved("\"quoted\"", "'\"quoted\"'")]
#[case::unicode("Straße", "'Straße'")]
fn escape_sql_string_cases(#[case] input: &str, #[case] expected: &str) {
assert_eq!(escape_sql_string(input), expected);
}
#[rstest]
#[case::wgs84(4326, "'EPSG:4326'")]
#[case::web_mercator(3857, "'EPSG:3857'")]
#[case::zero(0, "'EPSG:0'")]
#[case::negative(-1, "'EPSG:-1'")]
fn epsg_crs_cases(#[case] srid: i32, #[case] expected: &str) {
assert_eq!(epsg_crs(srid), expected);
}
#[rstest]
#[case::local_path("/data/buildings.parquet", "read_parquet('/data/buildings.parquet')")]
#[case::remote_url(
"https://example.org/data.parquet",
"read_parquet('https://example.org/data.parquet')"
)]
#[case::embedded_apostrophe("/data/O'Brien.parquet", "read_parquet('/data/O''Brien.parquet')")]
fn read_parquet_from_expr_cases(#[case] input: &str, #[case] expected: &str) {
assert_eq!(
format!("read_parquet({})", escape_sql_string(input)),
expected
);
}
}