Skip to main content

turso_sql/
iden.rs

1//! Identifiers for tables, columns and aliases, modeled by [`Ident`].
2//!
3//! Every name that reaches the writer goes through this module so that it is
4//! rendered double-quoted and with embedded quotes doubled; that is what makes
5//! reserved words, mixed case and user-supplied names safe to interpolate.
6//! The module owns the identifier types and their conversions only — it does
7//! not know how an identifier is rendered, which is the writer's business.
8//!
9//! - [`Iden`]: the trait a type implements to name something, implemented for
10//!   string types here and for column and table enums through the derive
11//!   macros of `turso-orm`;
12//! - [`Ident`]: an owned identifier backed by a `Cow` so static names never
13//!   allocate;
14//! - [`IntoIden`]: the conversion builders accept, so a `&'static str`, a
15//!   `String` or a derived enum can be passed interchangeably;
16//! - [`TableRef`] and [`ColumnRef`]: a table with its optional alias and a
17//!   column optionally qualified by table.
18
19use std::borrow::Cow;
20use std::fmt;
21
22/// Something that names a table, column or alias.
23///
24/// Implemented for string types and, through the derive macros in
25/// `turso-orm`, for column and table enums.
26pub trait Iden {
27    /// The unquoted identifier.
28    fn as_str(&self) -> &str;
29}
30
31impl Iden for &str {
32    fn as_str(&self) -> &str {
33        self
34    }
35}
36
37impl Iden for String {
38    fn as_str(&self) -> &str {
39        self
40    }
41}
42
43impl Iden for Ident {
44    fn as_str(&self) -> &str {
45        &self.0
46    }
47}
48
49/// An owned identifier.
50///
51/// The backing `Cow` lets names known at compile time be carried without an
52/// allocation, which matters because derived entity code names every column
53/// on every query.
54#[derive(Clone, Debug, PartialEq, Eq, Hash)]
55pub struct Ident(pub Cow<'static, str>);
56
57impl Ident {
58    /// Builds an identifier from a static string without allocating.
59    pub const fn new_static(name: &'static str) -> Self {
60        Ident(Cow::Borrowed(name))
61    }
62
63    /// The unquoted name.
64    pub fn name(&self) -> &str {
65        &self.0
66    }
67}
68
69impl fmt::Display for Ident {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        f.write_str(&self.0)
72    }
73}
74
75/// Conversion into an [`Ident`].
76///
77/// Builders take `impl IntoIden` so that callers can pass a `&'static str`, a
78/// `String`, an existing [`Ident`] or a reference to a derived identifier
79/// enum without converting by hand.
80pub trait IntoIden {
81    /// Converts into an owned identifier.
82    fn into_iden(self) -> Ident;
83}
84
85impl IntoIden for Ident {
86    fn into_iden(self) -> Ident {
87        self
88    }
89}
90
91impl IntoIden for &'static str {
92    fn into_iden(self) -> Ident {
93        Ident(Cow::Borrowed(self))
94    }
95}
96
97impl IntoIden for String {
98    fn into_iden(self) -> Ident {
99        Ident(Cow::Owned(self))
100    }
101}
102
103impl<T: Iden + ?Sized> IntoIden for &T
104where
105    T: IdenOwnedMarker,
106{
107    fn into_iden(self) -> Ident {
108        Ident(Cow::Owned(self.as_str().to_owned()))
109    }
110}
111
112/// Marker for reference conversions of custom [`Iden`] types.
113///
114/// A blanket `impl IntoIden for &T where T: Iden` would overlap with the
115/// `&'static str` implementation, so custom identifier types opt in through
116/// this marker instead; the derive macros of `turso-orm` implement it.
117pub trait IdenOwnedMarker {}
118
119impl From<&'static str> for Ident {
120    fn from(v: &'static str) -> Self {
121        v.into_iden()
122    }
123}
124
125impl From<String> for Ident {
126    fn from(v: String) -> Self {
127        v.into_iden()
128    }
129}
130
131/// A table reference with an optional alias.
132#[derive(Clone, Debug, PartialEq, Eq)]
133pub struct TableRef {
134    /// The table name.
135    pub name: Ident,
136    /// The `AS alias` part, when the table is aliased.
137    pub alias: Option<Ident>,
138}
139
140impl TableRef {
141    /// A table reference without alias.
142    pub fn new(name: impl IntoIden) -> Self {
143        Self {
144            name: name.into_iden(),
145            alias: None,
146        }
147    }
148
149    /// Sets the alias.
150    #[must_use]
151    pub fn alias(mut self, alias: impl IntoIden) -> Self {
152        self.alias = Some(alias.into_iden());
153        self
154    }
155
156    /// The identifier other clauses should use to refer to this table.
157    ///
158    /// Once a table is aliased, SQL requires every qualified column to use
159    /// the alias rather than the original name.
160    pub fn reference(&self) -> &Ident {
161        self.alias.as_ref().unwrap_or(&self.name)
162    }
163}
164
165impl<T: IntoIden> From<T> for TableRef {
166    fn from(name: T) -> Self {
167        TableRef::new(name)
168    }
169}
170
171/// A column reference, optionally qualified by table.
172#[derive(Clone, Debug, PartialEq, Eq)]
173pub enum ColumnRef {
174    /// A bare `column`.
175    Column(Ident),
176    /// A qualified `table.column`.
177    TableColumn(Ident, Ident),
178    /// The `*` wildcard.
179    Asterisk,
180    /// The `table.*` wildcard.
181    TableAsterisk(Ident),
182}
183
184impl<T: IntoIden> From<T> for ColumnRef {
185    fn from(name: T) -> Self {
186        ColumnRef::Column(name.into_iden())
187    }
188}
189
190impl<T: IntoIden, C: IntoIden> From<(T, C)> for ColumnRef {
191    fn from((table, column): (T, C)) -> Self {
192        ColumnRef::TableColumn(table.into_iden(), column.into_iden())
193    }
194}