1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7use use_sql_ident::{SqlIdentifier, SqlIdentifierError};
8use use_sql_schema::SqlSchemaName;
9
10#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub struct SqlTableName(SqlIdentifier);
13
14impl SqlTableName {
15 pub fn new(input: impl AsRef<str>) -> Result<Self, SqlTableError> {
21 SqlIdentifier::new(input)
22 .map(Self)
23 .map_err(SqlTableError::Identifier)
24 }
25
26 #[must_use]
28 pub fn as_str(&self) -> &str {
29 self.0.as_str()
30 }
31}
32
33impl AsRef<str> for SqlTableName {
34 fn as_ref(&self) -> &str {
35 self.as_str()
36 }
37}
38
39impl fmt::Display for SqlTableName {
40 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
41 self.0.fmt(formatter)
42 }
43}
44
45impl FromStr for SqlTableName {
46 type Err = SqlTableError;
47
48 fn from_str(input: &str) -> Result<Self, Self::Err> {
49 Self::new(input)
50 }
51}
52
53#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
55pub struct SqlTableAlias(SqlIdentifier);
56
57impl SqlTableAlias {
58 pub fn new(input: impl AsRef<str>) -> Result<Self, SqlTableError> {
64 SqlIdentifier::new(input)
65 .map(Self)
66 .map_err(SqlTableError::Identifier)
67 }
68
69 #[must_use]
71 pub fn as_str(&self) -> &str {
72 self.0.as_str()
73 }
74}
75
76impl fmt::Display for SqlTableAlias {
77 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78 self.0.fmt(formatter)
79 }
80}
81
82#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
84pub struct SqlTableRef {
85 schema: Option<SqlSchemaName>,
86 name: SqlTableName,
87 alias: Option<SqlTableAlias>,
88}
89
90impl SqlTableRef {
91 #[must_use]
93 pub const fn new(name: SqlTableName) -> Self {
94 Self {
95 schema: None,
96 name,
97 alias: None,
98 }
99 }
100
101 #[must_use]
103 pub fn with_schema(mut self, schema: SqlSchemaName) -> Self {
104 self.schema = Some(schema);
105 self
106 }
107
108 #[must_use]
110 pub fn with_alias(mut self, alias: SqlTableAlias) -> Self {
111 self.alias = Some(alias);
112 self
113 }
114
115 #[must_use]
117 pub const fn schema(&self) -> Option<&SqlSchemaName> {
118 self.schema.as_ref()
119 }
120
121 #[must_use]
123 pub const fn name(&self) -> &SqlTableName {
124 &self.name
125 }
126
127 #[must_use]
129 pub const fn alias(&self) -> Option<&SqlTableAlias> {
130 self.alias.as_ref()
131 }
132}
133
134impl fmt::Display for SqlTableRef {
135 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
136 if let Some(schema) = &self.schema {
137 write!(formatter, "{schema}.")?;
138 }
139 write!(formatter, "{}", self.name)?;
140 if let Some(alias) = &self.alias {
141 write!(formatter, " AS {alias}")?;
142 }
143 Ok(())
144 }
145}
146
147#[derive(Clone, Debug, Eq, PartialEq)]
149pub enum SqlTableError {
150 Identifier(SqlIdentifierError),
151}
152
153impl fmt::Display for SqlTableError {
154 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
155 match self {
156 Self::Identifier(error) => write!(formatter, "invalid SQL table identifier: {error}"),
157 }
158 }
159}
160
161impl Error for SqlTableError {}
162
163#[cfg(test)]
164mod tests {
165 use super::{SqlTableAlias, SqlTableError, SqlTableName, SqlTableRef};
166 use use_sql_schema::SqlSchemaName;
167
168 #[test]
169 fn creates_table_references() -> Result<(), Box<dyn std::error::Error>> {
170 let table = SqlTableRef::new(SqlTableName::new("users")?)
171 .with_schema(SqlSchemaName::new("public")?)
172 .with_alias(SqlTableAlias::new("u")?);
173
174 assert_eq!(table.to_string(), "public.users AS u");
175 Ok(())
176 }
177
178 #[test]
179 fn rejects_invalid_table_names() {
180 assert!(matches!(
181 SqlTableName::new(""),
182 Err(SqlTableError::Identifier(_))
183 ));
184 }
185}