Skip to content

Commit f827b75

Browse files
authored
Refactor conversions around qualified identifiers (#953)
There was some annoying repetition in this area. `TableName` and `ColumnName` each had three copy-pasted `From` impls. Now they have one, which will be easier to copy-paste when I add `TypeName` and `FunctionName`. I came up with this while re-doing #922 to add `TypeName`. Related to #927. `types.rs` is getting too long and starting to have visible disjoint "submodules", so I started splitting it. ## PR Info - Dependents: - #922 ## New Features - [x] The new traits are technically an addition, but I wouldn't advertise it. They are not meant to be used directly. I've documented this on the traits.
1 parent 4d8cb23 commit f827b75

File tree

2 files changed

+122
-79
lines changed

2 files changed

+122
-79
lines changed

src/types.rs renamed to src/types/mod.rs

Lines changed: 4 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ use crate::extension::postgres::PgBinOper;
88
#[cfg(feature = "backend-sqlite")]
99
use crate::extension::sqlite::SqliteBinOper;
1010

11+
mod qualification;
12+
13+
pub use qualification::{MaybeQualifiedOnce, MaybeQualifiedTwice};
14+
1115
/// A reference counted pointer: either [`Rc`][std::rc::Rc] or [`Arc`][std::sync::Arc],
1216
/// depending on the feature flags.
1317
///
@@ -556,85 +560,6 @@ where
556560
}
557561
}
558562

559-
impl<T> From<T> for SchemaName
560-
where
561-
T: IntoIden,
562-
{
563-
fn from(iden: T) -> Self {
564-
SchemaName(None, iden.into_iden())
565-
}
566-
}
567-
568-
impl<S, T> From<(S, T)> for SchemaName
569-
where
570-
S: IntoIden,
571-
T: IntoIden,
572-
{
573-
fn from((db, schema): (S, T)) -> Self {
574-
SchemaName(Some(db.into()), schema.into_iden())
575-
}
576-
}
577-
578-
impl<T> From<T> for TableName
579-
where
580-
T: IntoIden,
581-
{
582-
fn from(iden: T) -> Self {
583-
TableName(None, iden.into_iden())
584-
}
585-
}
586-
587-
impl<S, T> From<(S, T)> for TableName
588-
where
589-
S: IntoIden,
590-
T: IntoIden,
591-
{
592-
fn from((schema, table): (S, T)) -> Self {
593-
TableName(Some(schema.into()), table.into_iden())
594-
}
595-
}
596-
597-
impl<S, T, U> From<(S, T, U)> for TableName
598-
where
599-
S: IntoIden,
600-
T: IntoIden,
601-
U: IntoIden,
602-
{
603-
fn from((db, schema, table): (S, T, U)) -> Self {
604-
TableName(Some((db, schema).into()), table.into_iden())
605-
}
606-
}
607-
608-
impl<T> From<T> for ColumnName
609-
where
610-
T: IntoIden,
611-
{
612-
fn from(iden: T) -> Self {
613-
ColumnName(None, iden.into_iden())
614-
}
615-
}
616-
617-
impl<S, T> From<(S, T)> for ColumnName
618-
where
619-
S: IntoIden,
620-
T: IntoIden,
621-
{
622-
fn from((table, column): (S, T)) -> Self {
623-
ColumnName(Some(table.into()), column.into_iden())
624-
}
625-
}
626-
627-
impl<S, T, U> From<(S, T, U)> for ColumnName
628-
where
629-
S: IntoIden,
630-
T: IntoIden,
631-
U: IntoIden,
632-
{
633-
fn from((schema, table, column): (S, T, U)) -> Self {
634-
ColumnName(Some((schema, table).into()), column.into_iden())
635-
}
636-
}
637-
638563
impl<T> IntoColumnRef for T
639564
where
640565
T: Into<ColumnName>,

src/types/qualification.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
//! Conversion traits/impls for "potentially qualified" names like `(schema?).(table?).column`.
2+
3+
use super::*;
4+
5+
// -------------------------- MaybeQualifiedOnce -------------------------------
6+
7+
/// A name that can be unqualified (`foo`) or qualified once (`foo.bar`).
8+
///
9+
/// This is mostly a "private" helper trait to provide reusable conversions.
10+
pub trait MaybeQualifiedOnce {
11+
/// Represent a maybe-qualified name as a `(foo?, bar)` tuple.
12+
fn into_2_parts(self) -> (Option<DynIden>, DynIden);
13+
}
14+
15+
/// Only the "base", no qualification (`foo`).
16+
impl<T> MaybeQualifiedOnce for T
17+
where
18+
T: IntoIden,
19+
{
20+
fn into_2_parts(self) -> (Option<DynIden>, DynIden) {
21+
(None, self.into_iden())
22+
}
23+
}
24+
25+
/// With a qualification (`foo.bar`).
26+
impl<S, T> MaybeQualifiedOnce for (S, T)
27+
where
28+
S: IntoIden,
29+
T: IntoIden,
30+
{
31+
fn into_2_parts(self) -> (Option<DynIden>, DynIden) {
32+
let (qual, base) = self;
33+
(Some(qual.into_iden()), base.into_iden())
34+
}
35+
}
36+
37+
// ------------------------- MaybeQualifiedTwice -------------------------------
38+
39+
/// A name that can be unqualified (`foo`), qualified once (`foo.bar`), or twice (`foo.bar.baz`).
40+
///
41+
/// This is mostly a "private" helper trait to provide reusable conversions.
42+
pub trait MaybeQualifiedTwice {
43+
/// Represent a maybe-qualified name as a `(foo?, bar?, baz)` tuple.
44+
///
45+
/// To be precise, it's actually `((foo?, bar)?, baz)` to rule out invalid states like `(Some, None, Some)`.
46+
fn into_3_parts(self) -> (Option<(Option<DynIden>, DynIden)>, DynIden);
47+
}
48+
49+
/// From 1 or 2 parts (`foo` or `foo.bar`).
50+
impl<T> MaybeQualifiedTwice for T
51+
where
52+
T: MaybeQualifiedOnce,
53+
{
54+
fn into_3_parts(self) -> (Option<(Option<DynIden>, DynIden)>, DynIden) {
55+
let (middle, base) = self.into_2_parts();
56+
let qual = middle.map(|middle| (None, middle));
57+
(qual, base)
58+
}
59+
}
60+
61+
/// Fully-qualified from 3 parts (`foo.bar.baz`).
62+
impl<S, T, U> MaybeQualifiedTwice for (S, T, U)
63+
where
64+
S: IntoIden,
65+
T: IntoIden,
66+
U: IntoIden,
67+
{
68+
fn into_3_parts(self) -> (Option<(Option<DynIden>, DynIden)>, DynIden) {
69+
let (q2, q1, base) = self;
70+
let (q2, q1, base) = (q2.into_iden(), q1.into_iden(), base.into_iden());
71+
let q = (Some(q2), q1);
72+
(Some(q), base)
73+
}
74+
}
75+
76+
// -------------------------------- impls --------------------------------------
77+
78+
/// Construct a [`SchemaName`] from 1-2 parts (`(database?).schema`)
79+
impl<T> From<T> for SchemaName
80+
where
81+
T: MaybeQualifiedOnce,
82+
{
83+
fn from(value: T) -> Self {
84+
let (db, schema) = value.into_2_parts();
85+
let db_name = db.map(DatabaseName);
86+
SchemaName(db_name, schema)
87+
}
88+
}
89+
90+
/// Construct a [`TableName`] from 1-3 parts (`(database?).(schema?).table`)
91+
impl<T> From<T> for TableName
92+
where
93+
T: MaybeQualifiedTwice,
94+
{
95+
fn from(value: T) -> Self {
96+
let (schema_parts, table) = value.into_3_parts();
97+
let schema_name = schema_parts.map(|schema_parts| match schema_parts {
98+
(Some(db), schema) => SchemaName(Some(DatabaseName(db)), schema),
99+
(None, schema) => SchemaName(None, schema),
100+
});
101+
TableName(schema_name, table)
102+
}
103+
}
104+
105+
/// Construct a [`ColumnName`] from 1-3 parts (`(schema?).(table?).column`)
106+
impl<T> From<T> for ColumnName
107+
where
108+
T: MaybeQualifiedTwice,
109+
{
110+
fn from(value: T) -> Self {
111+
let (table_parts, column) = value.into_3_parts();
112+
let table_name = table_parts.map(|table_parts| match table_parts {
113+
(Some(schema), table) => TableName(Some(schema.into()), table),
114+
(None, table) => TableName(None, table),
115+
});
116+
ColumnName(table_name, column)
117+
}
118+
}

0 commit comments

Comments
 (0)