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_table::SqlTableName;
9
10#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub struct SqlColumnName(SqlIdentifier);
13
14impl SqlColumnName {
15 pub fn new(input: impl AsRef<str>) -> Result<Self, SqlColumnError> {
21 SqlIdentifier::new(input)
22 .map(Self)
23 .map_err(SqlColumnError::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 SqlColumnName {
34 fn as_ref(&self) -> &str {
35 self.as_str()
36 }
37}
38
39impl fmt::Display for SqlColumnName {
40 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
41 self.0.fmt(formatter)
42 }
43}
44
45impl FromStr for SqlColumnName {
46 type Err = SqlColumnError;
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 SqlColumnAlias(SqlIdentifier);
56
57impl SqlColumnAlias {
58 pub fn new(input: impl AsRef<str>) -> Result<Self, SqlColumnError> {
64 SqlIdentifier::new(input)
65 .map(Self)
66 .map_err(SqlColumnError::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 SqlColumnAlias {
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 SqlColumnRef {
85 table: Option<SqlTableName>,
86 name: SqlColumnName,
87 alias: Option<SqlColumnAlias>,
88}
89
90impl SqlColumnRef {
91 #[must_use]
93 pub const fn new(name: SqlColumnName) -> Self {
94 Self {
95 table: None,
96 name,
97 alias: None,
98 }
99 }
100
101 #[must_use]
103 pub const fn qualified(table: SqlTableName, name: SqlColumnName) -> Self {
104 Self {
105 table: Some(table),
106 name,
107 alias: None,
108 }
109 }
110
111 #[must_use]
113 pub fn with_alias(mut self, alias: SqlColumnAlias) -> Self {
114 self.alias = Some(alias);
115 self
116 }
117
118 #[must_use]
120 pub const fn table(&self) -> Option<&SqlTableName> {
121 self.table.as_ref()
122 }
123
124 #[must_use]
126 pub const fn name(&self) -> &SqlColumnName {
127 &self.name
128 }
129
130 #[must_use]
132 pub const fn alias(&self) -> Option<&SqlColumnAlias> {
133 self.alias.as_ref()
134 }
135}
136
137impl fmt::Display for SqlColumnRef {
138 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
139 if let Some(table) = &self.table {
140 write!(formatter, "{table}.")?;
141 }
142 write!(formatter, "{}", self.name)?;
143 if let Some(alias) = &self.alias {
144 write!(formatter, " AS {alias}")?;
145 }
146 Ok(())
147 }
148}
149
150#[derive(Clone, Debug, Eq, PartialEq)]
152pub enum SqlColumnError {
153 Identifier(SqlIdentifierError),
154}
155
156impl fmt::Display for SqlColumnError {
157 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
158 match self {
159 Self::Identifier(error) => write!(formatter, "invalid SQL column identifier: {error}"),
160 }
161 }
162}
163
164impl Error for SqlColumnError {}
165
166#[cfg(test)]
167mod tests {
168 use super::{SqlColumnAlias, SqlColumnError, SqlColumnName, SqlColumnRef};
169 use use_sql_table::SqlTableName;
170
171 #[test]
172 fn creates_column_references() -> Result<(), Box<dyn std::error::Error>> {
173 let column =
174 SqlColumnRef::qualified(SqlTableName::new("users")?, SqlColumnName::new("id")?)
175 .with_alias(SqlColumnAlias::new("user_id")?);
176
177 assert_eq!(column.to_string(), "users.id AS user_id");
178 Ok(())
179 }
180
181 #[test]
182 fn rejects_invalid_column_names() {
183 assert!(matches!(
184 SqlColumnName::new(""),
185 Err(SqlColumnError::Identifier(_))
186 ));
187 }
188}