use std::marker::PhantomData;
use super::foreign_key::CascadeAction;
use super::reverse::{ReverseRelationship, generate_reverse_accessor_singular};
#[derive(Debug, Clone)]
pub struct OneToOne<T, K> {
pub field_name: String,
pub to_field: String,
pub related_name: Option<String>,
pub on_delete: CascadeAction,
pub on_update: CascadeAction,
pub null: bool,
pub db_index: bool,
pub db_constraint: Option<String>,
pub parent_link: bool,
_phantom_t: PhantomData<T>,
_phantom_k: PhantomData<K>,
}
impl<T, K> OneToOne<T, K> {
pub fn new(field_name: impl Into<String>) -> Self {
Self {
field_name: field_name.into(),
to_field: "id".to_string(),
related_name: None,
on_delete: CascadeAction::default(),
on_update: CascadeAction::default(),
null: false,
db_index: true,
db_constraint: None,
parent_link: false,
_phantom_t: PhantomData,
_phantom_k: PhantomData,
}
}
pub fn to_field(mut self, to_field: impl Into<String>) -> Self {
self.to_field = to_field.into();
self
}
pub fn related_name(mut self, name: impl Into<String>) -> Self {
self.related_name = Some(name.into());
self
}
pub fn on_delete(mut self, action: CascadeAction) -> Self {
self.on_delete = action;
self
}
pub fn on_update(mut self, action: CascadeAction) -> Self {
self.on_update = action;
self
}
pub fn null(mut self, null: bool) -> Self {
self.null = null;
self
}
pub fn db_constraint(mut self, name: impl Into<String>) -> Self {
self.db_constraint = Some(name.into());
self
}
pub fn parent_link(mut self, parent_link: bool) -> Self {
self.parent_link = parent_link;
self
}
pub fn get_field_name(&self) -> &str {
&self.field_name
}
pub fn get_to_field(&self) -> &str {
&self.to_field
}
pub fn get_related_name(&self) -> Option<&str> {
self.related_name.as_deref()
}
pub fn get_on_delete(&self) -> CascadeAction {
self.on_delete
}
pub fn get_on_update(&self) -> CascadeAction {
self.on_update
}
pub fn is_null(&self) -> bool {
self.null
}
pub fn has_db_index(&self) -> bool {
self.db_index
}
pub fn get_db_constraint(&self) -> Option<&str> {
self.db_constraint.as_deref()
}
pub fn is_parent_link(&self) -> bool {
self.parent_link
}
}
impl<T, K> Default for OneToOne<T, K> {
fn default() -> Self {
Self::new("id")
}
}
impl<T, K> ReverseRelationship for OneToOne<T, K> {
fn get_or_generate_reverse_name(&self, model_name: &str) -> String {
self.related_name
.clone()
.unwrap_or_else(|| generate_reverse_accessor_singular(model_name))
}
fn explicit_reverse_name(&self) -> Option<&str> {
self.related_name.as_deref()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[allow(dead_code)]
#[derive(Clone)]
struct User {
id: i64,
name: String,
}
#[allow(dead_code)]
#[derive(Clone)]
struct UserProfile {
id: i64,
user_id: i64,
bio: String,
}
#[test]
fn test_one_to_one_creation() {
let rel: OneToOne<User, i64> = OneToOne::new("user_id");
assert_eq!(rel.get_field_name(), "user_id");
assert_eq!(rel.get_to_field(), "id");
assert_eq!(rel.get_related_name(), None);
assert_eq!(rel.get_on_delete(), CascadeAction::NoAction);
assert_eq!(rel.get_on_update(), CascadeAction::NoAction);
assert!(!rel.is_null());
assert!(rel.has_db_index());
assert!(!rel.is_parent_link());
}
#[test]
fn test_one_to_one_builder() {
let rel: OneToOne<User, i64> = OneToOne::new("user_id")
.related_name("profile")
.on_delete(CascadeAction::Cascade)
.on_update(CascadeAction::SetNull)
.null(true)
.db_constraint("fk_profile_user")
.parent_link(true);
assert_eq!(rel.get_field_name(), "user_id");
assert_eq!(rel.get_related_name(), Some("profile"));
assert_eq!(rel.get_on_delete(), CascadeAction::Cascade);
assert_eq!(rel.get_on_update(), CascadeAction::SetNull);
assert!(rel.is_null());
assert!(rel.has_db_index());
assert_eq!(rel.get_db_constraint(), Some("fk_profile_user"));
assert!(rel.is_parent_link());
}
#[test]
fn test_to_field_customization() {
let rel: OneToOne<User, i64> = OneToOne::new("user_id").to_field("uuid");
assert_eq!(rel.get_to_field(), "uuid");
}
#[test]
fn test_cascade_actions() {
let actions = vec![
CascadeAction::NoAction,
CascadeAction::Restrict,
CascadeAction::SetNull,
CascadeAction::SetDefault,
CascadeAction::Cascade,
];
for action in actions {
let rel: OneToOne<User, i64> = OneToOne::new("user_id").on_delete(action);
assert_eq!(rel.get_on_delete(), action);
}
}
#[test]
fn test_null_configuration() {
let rel1: OneToOne<User, i64> = OneToOne::new("user_id").null(true);
assert!(rel1.is_null());
let rel2: OneToOne<User, i64> = OneToOne::new("user_id").null(false);
assert!(!rel2.is_null());
}
#[test]
fn test_parent_link() {
let rel1: OneToOne<User, i64> = OneToOne::new("base_ptr_id").parent_link(true);
assert!(rel1.is_parent_link());
let rel2: OneToOne<User, i64> = OneToOne::new("user_id").parent_link(false);
assert!(!rel2.is_parent_link());
}
#[test]
fn test_db_index_always_true() {
let rel: OneToOne<User, i64> = OneToOne::new("user_id");
assert!(rel.has_db_index());
}
}