green_barrel/fields/
image.rs

1//! Field for uploading images.
2
3use core::fmt::Debug;
4use regex::Regex;
5use serde::{Deserialize, Serialize};
6use std::{error::Error, fs, path::Path};
7use uuid::Uuid;
8
9use crate::models::helpers::ImageData;
10
11#[derive(Serialize, Deserialize, Clone, Debug)]
12pub struct ImageField {
13    /// The value is determined automatically.
14    /// Format: "model-name--field-name".
15    pub id: String,
16    /// Web form field name.
17    pub label: String,
18    /// Field type.
19    pub field_type: String,
20    /// The value is determined automatically.
21    pub input_type: String,
22    /// The value is determined automatically.
23    pub name: String,
24    /// Sets the value of an element.
25    pub value: Option<ImageData>,
26    /// Value by default
27    pub default: Option<ImageData>,
28    /// Root partition for storing files.
29    pub media_root: String,
30    /// Url address to the root section.
31    pub media_url: String,
32    /// Directory for images inside media directory (inner path).
33    /// Example: "images/avatars".
34    pub target_dir: String,
35    /// Example: "image/jpeg,image/png,image/gif"
36    pub accept: String,
37    /// Displays prompt text.
38    pub placeholder: String,
39    /// Mandatory field.
40    pub required: bool,
41    /// Blocks access and modification of the element.
42    pub disabled: bool,
43    /// Specifies that the field cannot be modified by the user.   
44    pub readonly: bool,
45    /// From one to four inclusive.
46    /// Example: `vec![("xs", 150),("sm", 300),("md", 600),("lg", 1200)]`.
47    /// Hint: An Intel i7-4770 processor or better is recommended.
48    pub thumbnails: Vec<(String, u32)>,
49    /// Create thumbnails - Fast=false or qualitatively=true? Default = true.
50    pub is_quality: bool,
51    /// Hide field from user.
52    pub is_hide: bool,
53    /// Example: `r# "autofocus tabindex="some number" size="some number"#`.    
54    pub other_attrs: String,
55    /// Example: "class-name-1 class-name-2".
56    pub css_classes: String,
57    /// Additional explanation for the user.
58    pub hint: String,
59    /// Warning information.   
60    pub warning: String,
61    /// The value is determined automatically.
62    pub errors: Vec<String>,
63    /// To optimize field traversal in the `paladins/check()` method.
64    /// Hint: It is recommended not to change.
65    pub group: u32,
66}
67
68impl Default for ImageField {
69    fn default() -> Self {
70        Self {
71            id: String::new(),
72            label: String::new(),
73            field_type: String::from("ImageField"),
74            input_type: String::from("file"),
75            name: String::new(),
76            value: None,
77            default: None,
78            media_root: String::from("./resources/media"),
79            media_url: String::from("/media"),
80            target_dir: String::from("images"),
81            accept: String::new(),
82            placeholder: String::new(),
83            required: false,
84            disabled: false,
85            readonly: false,
86            thumbnails: Vec::new(),
87            is_quality: true,
88            is_hide: false,
89            other_attrs: String::new(),
90            css_classes: String::new(),
91            hint: String::new(),
92            warning: String::new(),
93            errors: Vec::new(),
94            group: 9,
95        }
96    }
97}
98
99impl ImageField {
100    /// Getter
101    pub fn get(&self) -> Option<ImageData> {
102        self.value.clone()
103    }
104    /// Setter
105    pub fn set(&mut self, image_path: &str, is_delete: bool, media_root: Option<&str>) {
106        if Regex::new(r"(?:(?:/|\\)\d{4}(?:/|\\)\d{2}(?:/|\\)\d{2}\-barrel(?:/|\\))")
107            .unwrap()
108            .is_match(image_path)
109        {
110            panic!("This image is not allowed to be reused - {image_path}")
111        }
112        let image_path = if !image_path.is_empty() {
113            Self::copy_file_to_tmp(image_path, media_root).unwrap()
114        } else {
115            String::new()
116        };
117        self.value = Some(ImageData {
118            path: image_path,
119            is_delete,
120            ..Default::default()
121        });
122    }
123    // Copy file to media_root}/tmp directory
124    pub fn copy_file_to_tmp(
125        image_path: &str,
126        media_root: Option<&str>,
127    ) -> Result<String, Box<dyn Error>> {
128        let media_root = if let Some(media_root) = media_root {
129            media_root.to_string()
130        } else {
131            "./resources/media".to_string()
132        };
133        let f_path = Path::new(image_path);
134        if !f_path.is_file() {
135            Err(format!("File is missing - {image_path}"))?
136        }
137        let dir_tmp = format!("{media_root}/tmp");
138        fs::create_dir_all(dir_tmp.clone())?;
139        let f_name = Uuid::new_v4().to_string();
140        let ext = f_path.extension().unwrap().to_str().unwrap();
141        let f_tmp = format!("{dir_tmp}/{f_name}.{ext}");
142        fs::copy(image_path, f_tmp.clone())?;
143        Ok(f_tmp)
144    }
145}