green_barrel/fields/
file.rs

1//! File upload field.
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::FileData;
10
11#[derive(Serialize, Deserialize, Clone, Debug)]
12pub struct FileField {
13    pub id: String, // The value is determined automatically. Format: "model-name--field-name".
14    pub label: String, // Web form field name.
15    pub field_type: String, // Field type.
16    pub input_type: String, // The value is determined automatically.
17    pub name: String, // The value is determined automatically.
18    pub value: Option<FileData>, // Sets the value of an element.
19    pub default: Option<FileData>, // Value by default
20    pub media_root: String, // Root partition for storing files.
21    pub media_url: String, // Url address to the root section.
22    pub target_dir: String, // Directory for files inside media directory (inner path). Example: "files/resume".
23    pub accept: String,     // Example: "image/jpeg,image/png,image/gif"
24    pub placeholder: String, // Displays prompt text.
25    pub required: bool,     // Mandatory field.
26    pub disabled: bool,     // Blocks access and modification of the element.
27    pub readonly: bool,     // Specifies that the field cannot be modified by the user.
28    pub is_hide: bool,      // Hide field from user.
29    /// Example: `r# "autofocus tabindex="some number" size="some number"#`.    
30    pub other_attrs: String,
31    pub css_classes: String, // Example: "class-name-1 class-name-2".
32    pub hint: String,        // Additional explanation for the user.
33    pub warning: String,     // Warning information.
34    pub errors: Vec<String>, // The value is determined automatically.
35    pub group: u32, // To optimize field traversal in the `paladins/check()` method. Hint: It is recommended not to change.
36}
37
38impl Default for FileField {
39    fn default() -> Self {
40        Self {
41            id: String::new(),
42            label: String::new(),
43            field_type: String::from("FileField"),
44            input_type: String::from("file"),
45            name: String::new(),
46            value: None,
47            default: None,
48            media_root: String::from("./resources/media"),
49            media_url: String::from("/media"),
50            target_dir: String::from("files"),
51            accept: String::new(),
52            placeholder: String::new(),
53            required: false,
54            disabled: false,
55            readonly: false,
56            is_hide: false,
57            other_attrs: String::new(),
58            css_classes: String::new(),
59            hint: String::new(),
60            warning: String::new(),
61            errors: Vec::new(),
62            group: 8,
63        }
64    }
65}
66
67impl FileField {
68    pub fn get(&self) -> Option<FileData> {
69        self.value.clone()
70    }
71    pub fn set(&mut self, file_path: &str, is_delete: bool, media_root: Option<&str>) {
72        if Regex::new(r"(?:(?:/|\\)\d{4}(?:/|\\)\d{2}(?:/|\\)\d{2}\-barrel(?:/|\\))")
73            .unwrap()
74            .is_match(file_path)
75        {
76            panic!("This file is not allowed to be reused - {file_path}")
77        }
78        let file_path = if !file_path.is_empty() {
79            Self::copy_file_to_tmp(file_path, media_root).unwrap()
80        } else {
81            String::new()
82        };
83        self.value = Some(FileData {
84            path: file_path,
85            is_delete,
86            ..Default::default()
87        });
88    }
89    // Copy file to media_root}/tmp directory
90    pub fn copy_file_to_tmp(
91        file_path: &str,
92        media_root: Option<&str>,
93    ) -> Result<String, Box<dyn Error>> {
94        let media_root = if let Some(media_root) = media_root {
95            media_root.to_string()
96        } else {
97            "./resources/media".to_string()
98        };
99        let f_path = Path::new(file_path);
100        if !f_path.is_file() {
101            Err(format!("File is missing - {file_path}"))?
102        }
103        let dir_tmp = format!("{media_root}/tmp");
104        fs::create_dir_all(dir_tmp.clone())?;
105        let f_name = Uuid::new_v4().to_string();
106        let ext = f_path.extension().unwrap().to_str().unwrap();
107        let f_tmp = format!("{dir_tmp}/{f_name}.{ext}");
108        fs::copy(file_path, f_tmp.clone())?;
109        Ok(f_tmp)
110    }
111}