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}