Skip to main content

use_sql_column/
lib.rs

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/// SQL column name primitive.
11#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub struct SqlColumnName(SqlIdentifier);
13
14impl SqlColumnName {
15    /// Creates a column name.
16    ///
17    /// # Errors
18    ///
19    /// Returns [`SqlColumnError`] when identifier validation fails.
20    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    /// Returns the column name text.
27    #[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/// SQL column alias primitive.
54#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
55pub struct SqlColumnAlias(SqlIdentifier);
56
57impl SqlColumnAlias {
58    /// Creates a column alias.
59    ///
60    /// # Errors
61    ///
62    /// Returns [`SqlColumnError`] when identifier validation fails.
63    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    /// Returns the alias text.
70    #[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/// SQL column reference metadata.
83#[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    /// Creates an unqualified column reference.
92    #[must_use]
93    pub const fn new(name: SqlColumnName) -> Self {
94        Self {
95            table: None,
96            name,
97            alias: None,
98        }
99    }
100
101    /// Creates a table-qualified column reference.
102    #[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    /// Adds a column alias.
112    #[must_use]
113    pub fn with_alias(mut self, alias: SqlColumnAlias) -> Self {
114        self.alias = Some(alias);
115        self
116    }
117
118    /// Returns the optional table qualifier.
119    #[must_use]
120    pub const fn table(&self) -> Option<&SqlTableName> {
121        self.table.as_ref()
122    }
123
124    /// Returns the column name.
125    #[must_use]
126    pub const fn name(&self) -> &SqlColumnName {
127        &self.name
128    }
129
130    /// Returns the optional alias.
131    #[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/// Error returned when SQL column metadata is invalid.
151#[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}