1use sql_orm_core::{Entity, EntityColumn};
2use sql_orm_query::{AggregateProjection, ColumnRef, Expr, SelectProjection, TableRef};
3use std::marker::PhantomData;
4
5#[derive(Debug)]
7pub struct AliasedEntityColumn<E: Entity> {
8 rust_field: &'static str,
9 column_name: &'static str,
10 alias: &'static str,
11 _entity: PhantomData<fn() -> E>,
12}
13
14impl<E: Entity> Copy for AliasedEntityColumn<E> {}
15
16impl<E: Entity> Clone for AliasedEntityColumn<E> {
17 fn clone(&self) -> Self {
18 *self
19 }
20}
21
22impl<E: Entity> PartialEq for AliasedEntityColumn<E> {
23 fn eq(&self, other: &Self) -> bool {
24 self.rust_field == other.rust_field
25 && self.column_name == other.column_name
26 && self.alias == other.alias
27 }
28}
29
30impl<E: Entity> Eq for AliasedEntityColumn<E> {}
31
32impl<E: Entity> AliasedEntityColumn<E> {
33 pub const fn new(column: EntityColumn<E>, alias: &'static str) -> Self {
34 Self {
35 rust_field: column.rust_field(),
36 column_name: column.column_name(),
37 alias,
38 _entity: PhantomData,
39 }
40 }
41
42 pub const fn column(&self) -> EntityColumn<E> {
43 EntityColumn::new(self.rust_field, self.column_name)
44 }
45
46 pub const fn alias(&self) -> &'static str {
47 self.alias
48 }
49
50 pub fn column_ref(self) -> ColumnRef {
51 ColumnRef::new(
52 TableRef::for_entity_as::<E>(self.alias),
53 self.rust_field,
54 self.column_name,
55 )
56 }
57
58 pub fn expr(self) -> Expr {
59 Expr::Column(self.column_ref())
60 }
61
62 pub fn table_ref(self) -> TableRef {
63 TableRef::for_entity_as::<E>(self.alias)
64 }
65}
66
67impl<E: Entity> From<AliasedEntityColumn<E>> for ColumnRef {
68 fn from(value: AliasedEntityColumn<E>) -> Self {
69 value.column_ref()
70 }
71}
72
73impl<E: Entity> From<AliasedEntityColumn<E>> for Expr {
74 fn from(value: AliasedEntityColumn<E>) -> Self {
75 value.expr()
76 }
77}
78
79impl<E: Entity> From<AliasedEntityColumn<E>> for SelectProjection {
80 fn from(value: AliasedEntityColumn<E>) -> Self {
81 let column_name = value.column_name;
82 SelectProjection::expr_as(value.expr(), column_name)
83 }
84}
85
86impl<E: Entity> From<AliasedEntityColumn<E>> for AggregateProjection {
87 fn from(value: AliasedEntityColumn<E>) -> Self {
88 let column_name = value.column_name;
89 AggregateProjection::group_key_as(value.expr(), column_name)
90 }
91}
92
93pub trait EntityColumnAliasExt<E: Entity> {
94 fn aliased(self, alias: &'static str) -> AliasedEntityColumn<E>;
95}
96
97impl<E: Entity> EntityColumnAliasExt<E> for EntityColumn<E> {
98 fn aliased(self, alias: &'static str) -> AliasedEntityColumn<E> {
99 AliasedEntityColumn::new(self, alias)
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::{AliasedEntityColumn, EntityColumnAliasExt};
106 use sql_orm_core::{
107 ColumnMetadata, Entity, EntityColumn, EntityMetadata, PrimaryKeyMetadata, SqlServerType,
108 };
109 use sql_orm_query::{AggregateProjection, ColumnRef, Expr, SelectProjection, TableRef};
110
111 struct TestEntity;
112
113 static TEST_ENTITY_COLUMNS: [ColumnMetadata; 1] = [ColumnMetadata {
114 rust_field: "name",
115 column_name: "name",
116 renamed_from: None,
117 sql_type: SqlServerType::NVarChar,
118 nullable: false,
119 primary_key: true,
120 identity: None,
121 default_sql: None,
122 computed_sql: None,
123 rowversion: false,
124 insertable: false,
125 updatable: false,
126 max_length: Some(120),
127 precision: None,
128 scale: None,
129 }];
130
131 static TEST_ENTITY_METADATA: EntityMetadata = EntityMetadata {
132 rust_name: "TestEntity",
133 schema: "dbo",
134 table: "test_entities",
135 renamed_from: None,
136 columns: &TEST_ENTITY_COLUMNS,
137 primary_key: PrimaryKeyMetadata {
138 name: None,
139 columns: &["name"],
140 },
141 indexes: &[],
142 foreign_keys: &[],
143 navigations: &[],
144 };
145
146 impl Entity for TestEntity {
147 fn metadata() -> &'static EntityMetadata {
148 &TEST_ENTITY_METADATA
149 }
150 }
151
152 #[allow(non_upper_case_globals)]
153 impl TestEntity {
154 const name: EntityColumn<TestEntity> = EntityColumn::new("name", "name");
155 }
156
157 #[test]
158 fn entity_column_can_be_bound_to_table_alias() {
159 let aliased: AliasedEntityColumn<TestEntity> = TestEntity::name.aliased("t");
160
161 assert_eq!(aliased.alias(), "t");
162 assert_eq!(aliased.column().rust_field(), TestEntity::name.rust_field());
163 assert_eq!(
164 aliased.column().column_name(),
165 TestEntity::name.column_name()
166 );
167 assert_eq!(
168 aliased.table_ref(),
169 TableRef::with_alias("dbo", "test_entities", "t")
170 );
171 assert_eq!(
172 aliased.column_ref(),
173 ColumnRef::new(
174 TableRef::with_alias("dbo", "test_entities", "t"),
175 "name",
176 "name"
177 )
178 );
179 assert_eq!(Expr::from(aliased), Expr::Column(aliased.column_ref()));
180 assert_eq!(
181 SelectProjection::from(aliased),
182 SelectProjection::expr_as(Expr::Column(aliased.column_ref()), "name")
183 );
184 assert_eq!(
185 AggregateProjection::from(aliased),
186 AggregateProjection::group_key_as(Expr::Column(aliased.column_ref()), "name")
187 );
188 }
189}