use crate::{
forms::{output_data::OutputDataForm, FileData, ImageData, Widget},
models::{caching::CachingModel, Meta, ToModel},
};
use rand::Rng;
use std::{fs, path::Path};
pub trait QPaladins: ToModel + CachingModel {
fn json_for_admin(&self) -> Result<String, Box<dyn std::error::Error>> {
let (form_cache, _client_cache) = Self::get_cache_data_for_query()?;
let meta: Meta = form_cache.meta;
let map_widgets = form_cache.map_widgets.clone();
let model_json = self.self_to_json()?;
let mut widget_list: Vec<Widget> = Vec::new();
let hash = self.get_hash().unwrap_or_default();
for field_name in meta.fields_name.iter() {
let mut widget = map_widgets.get(field_name).unwrap().clone();
if !field_name.contains("password") {
let field_json = model_json[field_name].clone();
if field_json.is_string() {
widget.value = field_json.as_str().unwrap().to_string();
} else if field_json.is_i64() {
widget.value = field_json.as_i64().unwrap().to_string();
} else if field_json.is_u64() {
widget.value = field_json.as_u64().unwrap().to_string();
} else if field_json.is_f64() {
widget.value = field_json.as_f64().unwrap().to_string();
} else if field_json.is_boolean() {
widget.checked = field_json.as_bool().unwrap();
} else if field_json.is_null() {
widget.value = String::new();
}
} else if !hash.is_empty() {
widget.widget = "hiddenText".to_string();
widget.input_type = "hidden".to_string();
widget.value = String::new();
}
widget_list.push(widget);
}
Ok(serde_json::to_string(&widget_list)?)
}
fn delete_file(
&self,
coll: &mongodb::sync::Collection,
model_name: &str,
field_name: &str,
) -> Result<(), Box<dyn std::error::Error>> {
let hash = self.get_hash().unwrap_or_default();
if !hash.is_empty() {
let object_id = mongodb::bson::oid::ObjectId::with_string(hash.as_str())?;
let filter = mongodb::bson::doc! {"_id": object_id};
if let Some(document) = coll.find_one(filter.clone(), None)? {
let file_doc = mongodb::bson::doc! {field_name: mongodb::bson::Bson::Null};
let update = mongodb::bson::doc! { "$set": file_doc };
coll.update_one(filter, update, None)?;
if let Some(field_file) = document.get(field_name).unwrap().as_document() {
let path = field_file.get_str("path")?;
let path = Path::new(path);
if path.exists() {
fs::remove_file(path)?;
}
} else {
Err(format!(
"Model: `{}` > Field: `{}` > Method: `delete_file()` : Document (file) not found.",
model_name, field_name
))?
}
} else {
Err(format!(
"Model: `{}` > Field: `{}` > Method: `delete_file()` : Document not found.",
model_name, field_name
))?
}
}
Ok(())
}
fn db_get_file_info(
&self,
coll: &mongodb::sync::Collection,
field_name: &str,
) -> Result<String, Box<dyn std::error::Error>> {
let hash = self.get_hash().unwrap_or_default();
let mut result = String::new();
if !hash.is_empty() {
let object_id = mongodb::bson::oid::ObjectId::with_string(hash.as_str())?;
let filter = mongodb::bson::doc! {"_id": object_id};
if let Some(document) = coll.find_one(filter, None)? {
if let Some(file_doc) = document.get(field_name).unwrap().as_document() {
result = serde_json::to_string(file_doc)?;
}
}
}
Ok(result)
}
fn check(&self) -> Result<OutputDataForm, Box<dyn std::error::Error>> {
let (form_cache, client_cache) = Self::get_cache_data_for_query()?;
let meta: Meta = form_cache.meta;
let model_name: &str = meta.model_name.as_str();
let mut is_err_symptom = if !meta.is_add_docs || !meta.is_up_docs {
true
} else {
false
};
let hash = self.get_hash().unwrap_or_default();
let hash = hash.as_str();
let is_update: bool = !hash.is_empty();
let ignore_fields: Vec<&str> = meta
.ignore_fields
.iter()
.map(|item| item.as_str())
.collect();
let coll: mongodb::sync::Collection = client_cache
.database(&meta.database_name)
.collection(&meta.collection_name);
let pre_json: serde_json::value::Value = self.self_to_json()?;
let mut final_doc = mongodb::bson::document::Document::new();
let fields_name: Vec<&str> = meta.fields_name.iter().map(|item| item.as_str()).collect();
let mut final_map_widgets: std::collections::HashMap<String, Widget> =
form_cache.map_widgets.clone();
{
let error_map = self.add_validation()?;
if !error_map.is_empty() {
is_err_symptom = true;
for (field_name, err_msg) in error_map {
if !fields_name.contains(&field_name) {
Err(format!(
"Model: `{}` > Method: `add_validation()` : \
The `{}` field is missing from the model.",
model_name, field_name
))?
}
if let Some(widget) = final_map_widgets.get_mut(&field_name.to_owned()) {
widget.error = Self::accumula_err(&widget, &err_msg.to_string())?;
}
}
}
}
for field_name in fields_name {
if field_name == "hash" {
if is_err_symptom {
let final_widget: &mut Widget = final_map_widgets.get_mut(field_name).unwrap();
if !meta.is_add_docs {
final_widget.common_msg = "It is forbidden to perform saves.".to_string();
} else if !meta.is_up_docs {
final_widget.common_msg = "It is forbidden to perform updates.".to_string();
}
}
continue;
}
let pre_json_value: Option<&serde_json::value::Value> = pre_json.get(field_name);
if pre_json_value.is_none() {
Err(format!(
"Model: `{}` > Field: `{}` > Method: `check()` : This field is missing.",
model_name, field_name
))?
}
let pre_json_value: &serde_json::value::Value = pre_json_value.unwrap();
let final_widget: &mut Widget = final_map_widgets.get_mut(field_name).unwrap();
let widget_type: &str = &final_widget.widget.clone()[..];
match widget_type {
"radioText" | "inputColor" | "inputEmail" | "inputPassword" | "inputPhone"
| "inputText" | "inputUrl" | "inputIP" | "inputIPv4" | "inputIPv6" | "textArea"
| "hiddenText" => {
if is_update && widget_type == "inputPassword" {
final_widget.value = String::new();
continue;
}
let mut field_value: String = if !pre_json_value.is_null() {
let clean_data: String =
pre_json_value.as_str().unwrap().trim().to_string();
if widget_type != "inputPassword" {
final_widget.value = clean_data.clone();
} else {
final_widget.value = String::new();
}
clean_data
} else {
String::new()
};
if field_value.is_empty() {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
final_widget.value = String::new();
continue;
} else {
if !is_update && widget_type != "inputPassword" {
if !final_widget.value.is_empty() {
field_value = final_widget.value.trim().to_string();
final_widget.value = String::new();
} else if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
final_widget.value = String::new();
continue;
} else {
final_widget.value = String::new();
continue;
}
} else {
final_widget.value = String::new();
continue;
}
}
}
let bson_field_value = if widget_type != "inputPassword" {
mongodb::bson::Bson::String(field_value.clone())
} else {
mongodb::bson::Bson::Null
};
let field_value: &str = field_value.as_str();
Self::check_minlength(final_widget.minlength, field_value).unwrap_or_else(
|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
},
);
Self::check_maxlength(final_widget.maxlength, field_value).unwrap_or_else(
|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
},
);
let min: f64 = final_widget.minlength.clone() as f64;
let max: f64 = final_widget.maxlength.clone() as f64;
let len: f64 = field_value.encode_utf16().count() as f64;
if (min > 0_f64 || max > 0_f64)
&& !validator::validate_range(
validator::Validator::Range {
min: Some(min),
max: Some(max),
},
len,
)
{
is_err_symptom = true;
let msg = format!(
"Length {} is out of range (min={} <> max={}).",
len, min, max
);
final_widget.error = Self::accumula_err(&final_widget, &msg).unwrap();
}
if widget_type != "inputPassword" && final_widget.unique {
Self::check_unique(hash, field_name, &bson_field_value, &coll)
.unwrap_or_else(|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
});
}
Self::regex_validation(widget_type, field_value).unwrap_or_else(|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
});
if !is_err_symptom && !ignore_fields.contains(&field_name) {
match widget_type {
"inputPassword" => {
if !field_value.is_empty() {
if !is_update {
let password_hash: String =
Self::create_password_hash(field_value)?;
final_doc.insert(
field_name,
mongodb::bson::Bson::String(password_hash),
);
}
}
}
_ => {
final_doc.insert(field_name, bson_field_value);
}
}
}
}
"inputDate" | "inputDateTime" => {
let mut field_value: String = if !pre_json_value.is_null() {
let clean_data: String =
pre_json_value.as_str().unwrap().trim().to_string();
final_widget.value = clean_data.clone();
clean_data
} else {
String::new()
};
if field_value.is_empty() {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
final_widget.value = String::new();
continue;
} else {
if !is_update {
if !final_widget.value.is_empty() {
field_value = final_widget.value.trim().to_string();
final_widget.value = String::new();
} else if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
final_widget.value = String::new();
continue;
} else {
final_widget.value = String::new();
continue;
}
} else {
final_widget.value = String::new();
continue;
}
}
}
let field_value: &str = field_value.as_str();
Self::regex_validation(widget_type, field_value).unwrap_or_else(|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
});
if is_err_symptom {
continue;
}
let dt_value: chrono::DateTime<chrono::Utc> = {
let field_value: String = if widget_type == "inputDate" {
format!("{}T00:00", field_value.to_string())
} else {
field_value.to_string()
};
chrono::DateTime::<chrono::Utc>::from_utc(
chrono::NaiveDateTime::parse_from_str(&field_value, "%Y-%m-%dT%H:%M")?,
chrono::Utc,
)
};
if final_widget.min != "0".to_string() && final_widget.max != "0".to_string() {
Self::regex_validation(widget_type, final_widget.min.as_str())
.unwrap_or_else(|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
});
Self::regex_validation(widget_type, final_widget.max.as_str())
.unwrap_or_else(|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
});
if is_err_symptom {
continue;
}
let dt_min: chrono::DateTime<chrono::Utc> = {
let min_value: String = if widget_type == "inputDate" {
format!("{}T00:00", final_widget.min.clone())
} else {
final_widget.min.clone()
};
chrono::DateTime::<chrono::Utc>::from_utc(
chrono::NaiveDateTime::parse_from_str(
&min_value,
"%Y-%m-%dT%H:%M",
)?,
chrono::Utc,
)
};
let dt_max: chrono::DateTime<chrono::Utc> = {
let max_value: String = if widget_type == "inputDate" {
format!("{}T00:00", final_widget.max.clone())
} else {
final_widget.max.clone()
};
chrono::DateTime::<chrono::Utc>::from_utc(
chrono::NaiveDateTime::parse_from_str(
&max_value,
"%Y-%m-%dT%H:%M",
)?,
chrono::Utc,
)
};
if dt_value < dt_min || dt_value > dt_max {
is_err_symptom = true;
final_widget.error = Self::accumula_err(
&final_widget,
&"Date out of range between `min` and` max`.".to_owned(),
)
.unwrap();
continue;
}
}
let dt_value_bson = mongodb::bson::Bson::DateTime(dt_value);
if final_widget.unique {
Self::check_unique(hash, field_name, &dt_value_bson, &coll).unwrap_or_else(
|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
},
);
}
if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, dt_value_bson);
}
}
"selectText" | "selectI32" | "selectU32" | "selectI64" | "selectF64"
| "selectTextDyn" | "selectI32Dyn" | "selectU32Dyn" | "selectI64Dyn"
| "selectF64Dyn" => {
if !pre_json_value.is_null() {
final_doc.insert(
field_name,
match widget_type {
"selectText" | "selectTextDyn" => {
let val = pre_json_value.as_str().unwrap().to_string();
final_widget.value = val.clone();
mongodb::bson::Bson::String(val)
}
"selectI32" | "selectI32Dyn" => {
let val = pre_json_value.as_i64().unwrap() as i32;
final_widget.value = val.to_string();
mongodb::bson::Bson::Int32(val)
}
"selectU32" | "selectI64" | "selectU32Dyn" | "selectI64Dyn" => {
let val = pre_json_value.as_i64().unwrap();
final_widget.value = val.to_string();
mongodb::bson::Bson::Int64(val)
}
"selectF64" | "selectF64Dyn" => {
let val = pre_json_value.as_f64().unwrap();
final_widget.value = val.to_string();
mongodb::bson::Bson::Double(val)
}
_ => Err(format!(
"Model: `{}` > Field: `{}` > Method: `check()` : \
Unsupported widget type - `{}`.",
model_name, field_name, widget_type
))?,
},
);
} else {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
} else if !ignore_fields.contains(&field_name) {
if !is_update {
if !final_widget.widget.contains("Dyn")
&& !final_widget.value.is_empty()
{
final_doc.insert(
field_name,
match widget_type {
"selectText" => {
let val = final_widget.value.trim().to_string();
mongodb::bson::Bson::String(val)
}
"selectI32" => {
let val = final_widget
.value
.trim()
.parse::<i32>()
.unwrap();
mongodb::bson::Bson::Int32(val)
}
"selectU32" | "selectI64" => {
let val = final_widget
.value
.trim()
.parse::<i64>()
.unwrap();
mongodb::bson::Bson::Int64(val)
}
"selectF64" => {
let val = final_widget
.value
.trim()
.parse::<f64>()
.unwrap();
mongodb::bson::Bson::Double(val)
}
_ => Err(format!(
"Model: `{}` > Field: `{}` > Method: `check()` : \
Unsupported widget type - `{}`.",
model_name, field_name, widget_type
))?,
},
);
} else {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
}
}
}
final_widget.value = String::new();
}
}
"selectTextMult" | "selectI32Mult" | "selectU32Mult" | "selectI64Mult"
| "selectF64Mult" | "selectTextMultDyn" | "selectI32MultDyn"
| "selectU32MultDyn" | "selectI64MultDyn" | "selectF64MultDyn" => {
if !pre_json_value.is_null() {
final_doc.insert(
field_name,
match widget_type {
"selectTextMult" | "selectTextMultDyn" => {
let val = pre_json_value
.as_array()
.unwrap()
.iter()
.map(|item| {
mongodb::bson::Bson::String(
item.as_str().unwrap().into(),
)
})
.collect::<Vec<mongodb::bson::Bson>>();
mongodb::bson::Bson::Array(val)
}
"selectI32Mult" | "selectI32MultDyn" => {
mongodb::bson::Bson::Array(
pre_json_value
.as_array()
.unwrap()
.iter()
.map(|item| {
mongodb::bson::Bson::Int32(
item.as_i64().unwrap() as i32
)
})
.collect::<Vec<mongodb::bson::Bson>>(),
)
}
"selectU32Mult" | "selectI64Mult" | "selectU32MultDyn"
| "selectI64MultDyn" => mongodb::bson::Bson::Array(
pre_json_value
.as_array()
.unwrap()
.iter()
.map(|item| {
mongodb::bson::Bson::Int64(item.as_i64().unwrap())
})
.collect::<Vec<mongodb::bson::Bson>>(),
),
"selectF64Mult" | "selectF64MultDyn" => mongodb::bson::Bson::Array(
pre_json_value
.as_array()
.unwrap()
.iter()
.map(|item| {
mongodb::bson::Bson::Double(item.as_f64().unwrap())
})
.collect::<Vec<mongodb::bson::Bson>>(),
),
_ => Err(format!(
"Model: `{}` > Field: `{}` > Method: `check()` : \
Unsupported widget type - `{}`.",
model_name, field_name, widget_type
))?,
},
);
} else {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
} else if !is_update {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
}
}
final_widget.value = String::new();
}
"inputFile" => {
let mut field_value: FileData = if !pre_json_value.is_null() {
let obj_str = pre_json_value.as_str().unwrap();
if let Some(is_delete) = serde_json::from_str::<
serde_json::map::Map<String, serde_json::Value>,
>(obj_str)
.unwrap()
.get("is_delete")
{
if is_update && is_delete.as_bool().unwrap() {
self.delete_file(&coll, model_name, field_name)?;
final_doc.insert(field_name, mongodb::bson::Bson::Null);
}
}
serde_json::from_str::<FileData>(obj_str)?
} else {
FileData::default()
};
if field_value.path.is_empty() && field_value.url.is_empty() {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
final_widget.value = self.db_get_file_info(&coll, field_name)?;
continue;
} else {
if !is_update {
if !final_widget.value.is_empty() {
field_value = serde_json::from_str(final_widget.value.trim())?;
} else if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
final_widget.value =
self.db_get_file_info(&coll, field_name)?;
continue;
} else {
final_widget.value =
self.db_get_file_info(&coll, field_name)?;
continue;
}
} else {
final_widget.value = self.db_get_file_info(&coll, field_name)?;
continue;
}
}
}
final_widget.value = self.db_get_file_info(&coll, field_name)?;
let is_emty_path = field_value.path.is_empty();
let is_emty_url = field_value.url.is_empty();
if (!is_emty_path && is_emty_url) || (is_emty_path && !is_emty_url) {
Err(format!(
"Model: `{}` > Field: `{}` > Method: \
`check()` : Incorrectly filled field. \
Example (for default): {{\"path\":\"./media/hello_world.odt\",\"url\":\"/media/hello_world.odt\"}} \
Example (from client side): {{\"path\":\"./media/hello_world.odt\",\"url\":\"/media/hello_world.odt\",\"is_delete\":false}}",
model_name, field_name
))?
}
let path: String = field_value.path.clone();
let f_path = std::path::Path::new(path.as_str());
if !f_path.exists() || !f_path.is_file() {
Err(format!(
"Model: `{}` > Field: `{}` > Method: \
`check()` : File is missing - {}",
model_name, field_name, path
))?
}
let metadata: std::fs::Metadata = f_path.metadata()?;
field_value.size = metadata.len() as u32;
field_value.name = f_path.file_name().unwrap().to_str().unwrap().to_string();
if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_widget.value = serde_json::to_string(&field_value)?;
let bson_field_value = mongodb::bson::ser::to_bson(&field_value.clone())?;
final_doc.insert(field_name, bson_field_value);
}
}
"inputImage" => {
let mut field_value: ImageData = if !pre_json_value.is_null() {
let obj_str = pre_json_value.as_str().unwrap();
if let Some(is_delete) = serde_json::from_str::<
serde_json::map::Map<String, serde_json::Value>,
>(obj_str)
.unwrap()
.get("is_delete")
{
if is_update && is_delete.as_bool().unwrap() {
self.delete_file(&coll, model_name, field_name)?;
final_doc.insert(field_name, mongodb::bson::Bson::Null);
}
}
serde_json::from_str::<ImageData>(obj_str)?
} else {
ImageData::default()
};
if field_value.path.is_empty() && field_value.url.is_empty() {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
final_widget.value = self.db_get_file_info(&coll, field_name)?;
continue;
} else {
if !is_update {
if !final_widget.value.is_empty() {
field_value = serde_json::from_str(final_widget.value.trim())?;
} else if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
final_widget.value =
self.db_get_file_info(&coll, field_name)?;
continue;
} else {
final_widget.value =
self.db_get_file_info(&coll, field_name)?;
continue;
}
} else {
final_widget.value = self.db_get_file_info(&coll, field_name)?;
continue;
}
}
}
final_widget.value = self.db_get_file_info(&coll, field_name)?;
let is_emty_path = field_value.path.is_empty();
let is_emty_url = field_value.url.is_empty();
if (!is_emty_path && is_emty_url) || (is_emty_path && !is_emty_url) {
Err(format!(
"Model: `{}` > Field: `{}` > Method: \
`check()` : Incorrectly filled field. \
Example (for default): {{\"path\":\"./media/no-image-found.png\",\"url\":\"/media/no-image-found.png\"}} \
Example (from client side): {{\"path\":\"./media/no-image-found.png\",\"url\":\"/media/no-image-found.png\",\"is_delete\":false}}",
model_name, field_name
))?
}
let path: String = field_value.path.clone();
let f_path = std::path::Path::new(path.as_str());
if !f_path.exists() || !f_path.is_file() {
Err(format!(
"Model: `{}` > Field: `{}` > Method: \
`check()` : File is missing - {}",
model_name, field_name, path
))?
}
let metadata: std::fs::Metadata = f_path.metadata()?;
field_value.size = metadata.len() as u32;
field_value.name = f_path.file_name().unwrap().to_str().unwrap().to_string();
let dimensions: (u32, u32) = image::image_dimensions(path)?;
field_value.width = dimensions.0;
field_value.height = dimensions.1;
if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_widget.value = serde_json::to_string(&field_value)?;
let bson_field_value = mongodb::bson::ser::to_bson(&field_value.clone())?;
final_doc.insert(field_name, bson_field_value);
}
}
"radioI32" | "numberI32" | "rangeI32" | "hiddenI32" => {
let mut field_value: Option<i64> = pre_json_value.as_i64();
let is_null_value: bool = pre_json_value.is_null();
if is_null_value {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
final_widget.value = String::new();
continue;
} else if is_null_value {
if !is_update {
if !final_widget.value.is_empty() {
field_value =
Some(final_widget.value.trim().parse::<i64>().unwrap());
final_widget.value = String::new();
} else if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
final_widget.value = String::new();
continue;
} else {
final_widget.value = String::new();
continue;
}
} else {
final_widget.value = String::new();
continue;
}
}
}
let field_value: i32 = field_value.unwrap() as i32;
if !is_null_value {
final_widget.value = field_value.to_string();
}
let bson_field_value = mongodb::bson::Bson::Int32(field_value);
if final_widget.unique {
Self::check_unique(hash, field_name, &bson_field_value, &coll)
.unwrap_or_else(|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
});
}
let min: f64 = final_widget.min.parse().unwrap();
let max: f64 = final_widget.max.parse().unwrap();
let num: f64 = field_value as f64;
if (min > 0_f64 || max > 0_f64)
&& !validator::validate_range(
validator::Validator::Range {
min: Some(min),
max: Some(max),
},
num,
)
{
is_err_symptom = true;
let msg = format!(
"Number {} is out of range (min={} <> max={}).",
num, min, max
);
final_widget.error = Self::accumula_err(&final_widget, &msg).unwrap();
}
if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, bson_field_value);
}
}
"radioU32" | "numberU32" | "rangeU32" | "checkBoxI64" | "radioI64"
| "numberI64" | "rangeI64" | "hiddenU32" | "hiddenI64" => {
let mut field_value: Option<i64> = pre_json_value.as_i64();
let is_null_value: bool = pre_json_value.is_null();
if is_null_value {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
final_widget.value = String::new();
continue;
} else if is_null_value {
if !is_update {
if !final_widget.value.is_empty() {
field_value =
Some(final_widget.value.trim().parse::<i64>().unwrap());
final_widget.value = String::new();
} else if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
final_widget.value = String::new();
continue;
} else {
final_widget.value = String::new();
continue;
}
} else {
final_widget.value = String::new();
continue;
}
}
}
let field_value: i64 = field_value.unwrap();
if !is_null_value {
final_widget.value = field_value.to_string();
}
let bson_field_value = mongodb::bson::Bson::Int64(field_value);
if final_widget.unique {
Self::check_unique(hash, field_name, &bson_field_value, &coll)
.unwrap_or_else(|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
});
}
let min: f64 = final_widget.min.parse().unwrap();
let max: f64 = final_widget.max.parse().unwrap();
let num: f64 = field_value as f64;
if (min > 0_f64 || max > 0_f64)
&& !validator::validate_range(
validator::Validator::Range {
min: Some(min),
max: Some(max),
},
num,
)
{
is_err_symptom = true;
let msg = format!(
"Number {} is out of range (min={} <> max={}).",
num, min, max
);
final_widget.error = Self::accumula_err(&final_widget, &msg).unwrap();
}
if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, bson_field_value);
}
}
"radioF64" | "numberF64" | "rangeF64" | "hiddenF64" => {
let mut field_value: Option<f64> = pre_json_value.as_f64();
let is_null_value: bool = pre_json_value.is_null();
if is_null_value {
if final_widget.required {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &"Required field.".to_owned())
.unwrap();
final_widget.value = String::new();
continue;
} else if is_null_value {
if !is_update {
if !final_widget.value.is_empty() {
field_value =
Some(final_widget.value.trim().parse::<f64>().unwrap());
final_widget.value = String::new();
} else if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, mongodb::bson::Bson::Null);
final_widget.value = String::new();
continue;
} else {
final_widget.value = String::new();
continue;
}
} else {
final_widget.value = String::new();
continue;
}
}
}
let field_value: f64 = field_value.unwrap();
if !is_null_value {
final_widget.value = field_value.to_string();
}
let bson_field_value = mongodb::bson::Bson::Double(field_value);
if final_widget.unique {
Self::check_unique(hash, field_name, &bson_field_value, &coll)
.unwrap_or_else(|err| {
is_err_symptom = true;
final_widget.error =
Self::accumula_err(&final_widget, &err.to_string()).unwrap();
});
}
let min: f64 = final_widget.min.parse().unwrap();
let max: f64 = final_widget.max.parse().unwrap();
let num: f64 = field_value.clone();
if (min > 0_f64 || max > 0_f64)
&& !validator::validate_range(
validator::Validator::Range {
min: Some(min),
max: Some(max),
},
num,
)
{
is_err_symptom = true;
let msg = format!(
"Number {} is out of range (min={} <> max={}).",
num, min, max
);
final_widget.error = Self::accumula_err(&final_widget, &msg).unwrap();
}
if !is_err_symptom && !ignore_fields.contains(&field_name) {
final_doc.insert(field_name, bson_field_value);
}
}
"checkBox" => {
let field_value: bool = if !pre_json_value.is_null() {
pre_json_value.as_bool().unwrap()
} else {
if final_widget.required {
is_err_symptom = true;
final_widget.error = Self::accumula_err(
&final_widget,
&"You must definitely choose.".to_owned(),
)
.unwrap();
false
} else {
final_widget.checked
}
};
final_widget.checked = field_value.clone();
if !is_err_symptom && !ignore_fields.contains(&field_name) {
let bson_field_value = mongodb::bson::Bson::Boolean(field_value);
final_doc.insert(field_name, bson_field_value);
}
}
_ => Err(format!(
"Model: `{}` > Field: `{}` > Method: `check()` : \
Unsupported widget type - `{}`.",
model_name, field_name, widget_type
))?,
}
if !is_err_symptom {
let dt: chrono::DateTime<chrono::Utc> = chrono::Utc::now();
if !is_update {
final_doc.insert("created_at", mongodb::bson::Bson::DateTime(dt));
final_doc.insert("updated_at", mongodb::bson::Bson::DateTime(dt));
} else {
final_doc.insert("updated_at", mongodb::bson::Bson::DateTime(dt));
}
}
if !is_err_symptom && !is_update {
final_doc.insert("paperclip", mongodb::bson::Bson::Null);
}
}
Self::vitaminize(
meta.project_name.as_str(),
meta.unique_project_key.as_str(),
meta.collection_name.as_str(),
&client_cache,
&mut final_map_widgets,
)?;
Ok(OutputDataForm::CheckModel((
!is_err_symptom,
meta.fields_name.clone(),
final_map_widgets,
final_doc,
)))
}
fn save(
&mut self,
options_insert: Option<mongodb::options::InsertOneOptions>,
options_update: Option<mongodb::options::UpdateOptions>,
) -> Result<OutputDataForm, Box<dyn std::error::Error>> {
let verified_data: OutputDataForm = self.check()?;
let is_no_error: bool = verified_data.bool();
let (form_cache, client_cache) = Self::get_cache_data_for_query()?;
let meta: Meta = form_cache.meta;
let mut final_map_widgets: std::collections::HashMap<String, Widget> = verified_data.wig();
let is_update: bool = !self.get_hash().unwrap_or_default().is_empty();
let coll: mongodb::sync::Collection = client_cache
.database(meta.database_name.as_str())
.collection(meta.collection_name.as_str());
if is_no_error {
let final_doc = verified_data.doc();
if !is_update {
let result: mongodb::results::InsertOneResult =
coll.insert_one(final_doc, options_insert)?;
self.set_hash(result.inserted_id.as_object_id().unwrap().to_hex());
} else if !final_doc.is_empty() {
let hash: Option<String> = self.get_hash();
if hash.is_none() {
Err(format!(
"Model: `{}` > Field: `hash` : \
An empty `hash` field is not allowed when updating.",
meta.model_name
))?
}
let object_id: mongodb::bson::oid::ObjectId =
mongodb::bson::oid::ObjectId::with_string(hash.unwrap().as_str())?;
let query: mongodb::bson::document::Document =
mongodb::bson::doc! {"_id": object_id};
let update: mongodb::bson::document::Document = mongodb::bson::doc! {
"$set": final_doc,
};
coll.update_one(query, update, options_update)?;
}
}
let hash = self.get_hash().unwrap_or_default();
if !hash.is_empty() {
final_map_widgets.get_mut(&"hash".to_owned()).unwrap().value = hash.clone();
}
Ok(OutputDataForm::Save((
is_no_error,
meta.fields_name.clone(),
final_map_widgets,
hash,
)))
}
fn delete(
&self,
options: Option<mongodb::options::DeleteOptions>,
) -> Result<OutputDataForm, Box<dyn std::error::Error>> {
let (form_cache, client_cache) = Self::get_cache_data_for_query()?;
let meta: Meta = form_cache.meta;
let is_permission_delete: bool = meta.is_del_docs;
let err_msg = if is_permission_delete {
String::new()
} else {
"It is forbidden to perform delete.".to_string()
};
let result_bool = if is_permission_delete {
let coll: mongodb::sync::Collection = client_cache
.database(meta.database_name.as_str())
.collection(meta.collection_name.as_str());
let hash: Option<String> = self.get_hash();
if hash.is_none() {
Err(format!(
"Model: `{}` > Field: `hash` : \
An empty `hash` field is not allowed when deleting.",
meta.model_name
))?
}
let object_id: mongodb::bson::oid::ObjectId =
mongodb::bson::oid::ObjectId::with_string(hash.unwrap().as_str())?;
let query: mongodb::bson::document::Document = mongodb::bson::doc! {"_id": object_id};
coll.delete_one(query, options).is_ok()
} else {
false
};
Ok(OutputDataForm::Delete((result_bool, err_msg)))
}
fn create_password_hash(field_value: &str) -> Result<String, Box<dyn std::error::Error>> {
const CHARSET: &[u8] =
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%^&+=*!~)(";
const SALT_LEN: usize = 12;
let mut rng = rand::thread_rng();
let password: &[u8] = field_value.as_bytes();
let salt: String = (0..SALT_LEN)
.map(|_| {
let idx = rng.gen_range(0, CHARSET.len());
CHARSET[idx] as char
})
.collect();
let salt: &[u8] = salt.as_bytes();
let config = argon2::Config::default();
let hash: String = argon2::hash_encoded(password, salt, &config)?;
Ok(hash)
}
fn verify_password(
&self,
password: &str,
options: Option<mongodb::options::FindOneOptions>,
) -> Result<bool, Box<dyn std::error::Error>> {
let (form_cache, client_cache) = Self::get_cache_data_for_query()?;
let meta: Meta = form_cache.meta;
let coll: mongodb::sync::Collection = client_cache
.database(meta.database_name.as_str())
.collection(meta.collection_name.as_str());
let hash: Option<String> = self.get_hash();
if hash.is_none() {
Err(format!(
"Model: `{}` > Method: `verify_password` : \
An empty `hash` field is not allowed when updating.",
meta.model_name
))?
}
let object_id: mongodb::bson::oid::ObjectId =
mongodb::bson::oid::ObjectId::with_string(hash.unwrap().as_str())?;
let filter: mongodb::bson::document::Document = mongodb::bson::doc! {"_id": object_id};
let doc = coll.find_one(filter, options)?;
if doc.is_none() {
Err(format!(
"Model: `{}` > Method: `verify_password` : \
There is no document in the database for the current `hash` value.",
meta.model_name
))?
}
let doc = doc.unwrap();
let password_hash = doc.get("password");
if password_hash.is_none() {
Err(format!(
"Model: `{}` > Method: `verify_password` : \
The password field is missing.",
meta.model_name
))?
}
let password_hash = password_hash.unwrap();
let password_hash = if password_hash != &mongodb::bson::Bson::Null {
password_hash.as_str().unwrap()
} else {
""
};
Ok(argon2::verify_encoded(password_hash, password.as_bytes())?)
}
fn update_password(
&self,
old_password: &str,
new_password: &str,
options_find_old: Option<mongodb::options::FindOneOptions>,
options_update: Option<mongodb::options::UpdateOptions>,
) -> Result<bool, Box<dyn std::error::Error>> {
if !self.verify_password(old_password, options_find_old)? {
return Ok(false);
}
let (form_cache, client_cache) = Self::get_cache_data_for_query()?;
let meta: Meta = form_cache.meta;
let coll: mongodb::sync::Collection = client_cache
.database(meta.database_name.as_str())
.collection(meta.collection_name.as_str());
let hash = self.get_hash().unwrap();
let object_id: mongodb::bson::oid::ObjectId =
mongodb::bson::oid::ObjectId::with_string(hash.as_str())?;
let query: mongodb::bson::document::Document = mongodb::bson::doc! {"_id": object_id};
let new_password_hash = Self::create_password_hash(new_password)?;
let doc = mongodb::bson::doc! {"password": new_password_hash};
let update: mongodb::bson::document::Document = mongodb::bson::doc! {
"$set": doc,
};
Ok(coll
.update_one(query, update, options_update)?
.modified_count
== 1_i64)
}
}