karbon_framework/validation/
async_validator.rs1use crate::db::{DbPool, placeholder};
2use crate::error::{AppError, AppResult};
3
4pub struct AsyncValidator<'a> {
30 pool: &'a DbPool,
31 rules: Vec<ValidationRule>,
32}
33
34enum ValidationRule {
35 Unique {
36 table: String,
37 column: String,
38 value: String,
39 except_id: Option<i64>,
40 message: String,
41 },
42 Exists {
43 table: String,
44 column: String,
45 value: i64,
46 message: String,
47 },
48}
49
50impl<'a> AsyncValidator<'a> {
51 pub fn new(pool: &'a DbPool) -> Self {
52 Self {
53 pool,
54 rules: Vec::new(),
55 }
56 }
57
58 pub fn unique(mut self, table: &str, column: &str, value: &str, message: &str) -> Self {
61 self.rules.push(ValidationRule::Unique {
62 table: table.to_string(),
63 column: column.to_string(),
64 value: value.to_string(),
65 except_id: None,
66 message: message.to_string(),
67 });
68 self
69 }
70
71 pub fn unique_except(mut self, table: &str, column: &str, value: &str, except_id: i64, message: &str) -> Self {
74 self.rules.push(ValidationRule::Unique {
75 table: table.to_string(),
76 column: column.to_string(),
77 value: value.to_string(),
78 except_id: Some(except_id),
79 message: message.to_string(),
80 });
81 self
82 }
83
84 pub fn unique_except_if_some(self, table: &str, column: &str, value: &Option<String>, except_id: i64, message: &str) -> Self {
87 match value {
88 Some(v) => self.unique_except(table, column, v, except_id, message),
89 None => self,
90 }
91 }
92
93 pub fn exists(mut self, table: &str, column: &str, value: i64, message: &str) -> Self {
96 self.rules.push(ValidationRule::Exists {
97 table: table.to_string(),
98 column: column.to_string(),
99 value,
100 message: message.to_string(),
101 });
102 self
103 }
104
105 pub async fn validate(self) -> AppResult<()> {
107 for rule in &self.rules {
108 match rule {
109 ValidationRule::Unique { table, column, value, except_id, message } => {
110 let sql = match except_id {
111 Some(_) => format!(
112 "SELECT COUNT(*) FROM {} WHERE {} = {} AND id != {}",
113 table, column, placeholder(1), placeholder(2)
114 ),
115 None => format!(
116 "SELECT COUNT(*) FROM {} WHERE {} = {}",
117 table, column, placeholder(1)
118 ),
119 };
120
121 let mut query = sqlx::query_as::<_, (i64,)>(&sql).bind(value);
122 if let Some(id) = except_id {
123 query = query.bind(*id);
124 }
125
126 let (count,) = query.fetch_one(self.pool).await?;
127 if count > 0 {
128 return Err(AppError::Conflict(message.clone()));
129 }
130 }
131 ValidationRule::Exists { table, column, value, message } => {
132 let sql = format!(
133 "SELECT COUNT(*) FROM {} WHERE {} = {}",
134 table, column, placeholder(1)
135 );
136 let (count,): (i64,) = sqlx::query_as(&sql)
137 .bind(*value)
138 .fetch_one(self.pool)
139 .await?;
140 if count == 0 {
141 return Err(AppError::NotFound(message.clone()));
142 }
143 }
144 }
145 }
146 Ok(())
147 }
148}