use std::fmt;
use std::marker::PhantomData;
pub struct Column<R, V> {
name: &'static str,
_phantom: PhantomData<fn(R) -> V>,
}
impl<R, V> Column<R, V> {
pub const fn new(name: &'static str) -> Self {
Self {
name,
_phantom: PhantomData,
}
}
pub const fn name(&self) -> &'static str {
self.name
}
}
impl<R, V> Clone for Column<R, V> {
fn clone(&self) -> Self {
*self
}
}
impl<R, V> Copy for Column<R, V> {}
impl<R, V> fmt::Debug for Column<R, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Column").field("name", &self.name).finish()
}
}
impl<R, V> PartialEq for Column<R, V> {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl<R, V> Eq for Column<R, V> {}
pub trait IntoColumnName<R> {
fn into_column_name(self) -> String;
}
impl<R, V> IntoColumnName<R> for Column<R, V> {
fn into_column_name(self) -> String {
self.name.to_string()
}
}
impl<R> IntoColumnName<R> for &str {
fn into_column_name(self) -> String {
self.to_string()
}
}
impl<R> IntoColumnName<R> for String {
fn into_column_name(self) -> String {
self
}
}
impl<R> IntoColumnName<R> for &String {
fn into_column_name(self) -> String {
self.clone()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
struct Posts;
#[allow(dead_code)]
struct Users;
#[test]
fn column_name_round_trips() {
let c: Column<Posts, String> = Column::new("status");
assert_eq!(c.name(), "status");
}
#[test]
fn column_is_copy_and_clone() {
let c: Column<Posts, i32> = Column::new("view_count");
let copied = c;
#[allow(clippy::clone_on_copy)]
let cloned = c.clone();
assert_eq!(copied.name(), "view_count");
assert_eq!(cloned.name(), "view_count");
assert_eq!(c.name(), "view_count");
}
#[test]
fn column_debug_does_not_panic() {
let c: Column<Posts, String> = Column::new("id");
let s = format!("{c:?}");
assert!(s.contains("Column"), "{s}");
assert!(s.contains("id"), "{s}");
}
#[test]
fn column_partial_eq_compares_by_name_only() {
let a: Column<Posts, String> = Column::new("id");
let b: Column<Posts, String> = Column::new("id");
let c: Column<Posts, String> = Column::new("name");
assert_eq!(a, b);
assert_ne!(a, c);
}
fn _type_parameters_are_distinct() {
let _p: Column<Posts, String> = Column::new("id");
let _u: Column<Users, String> = Column::new("id");
}
#[test]
fn into_column_name_for_typed_column() {
let c: Column<Posts, i32> = Column::new("view_count");
assert_eq!(IntoColumnName::<Posts>::into_column_name(c), "view_count");
}
#[test]
fn into_column_name_for_str() {
let s: &str = "raw_column";
assert_eq!(IntoColumnName::<Posts>::into_column_name(s), "raw_column");
}
#[test]
fn into_column_name_for_string() {
let s = String::from("owned_column");
assert_eq!(
IntoColumnName::<Posts>::into_column_name(s),
"owned_column"
);
}
#[test]
fn into_column_name_for_string_ref() {
let s = String::from("ref_column");
assert_eq!(
IntoColumnName::<Posts>::into_column_name(&s),
"ref_column"
);
}
}