1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use crate::{exec_cmd, Arguments, Config, MyResult};
use std::{path::PathBuf, process::Command};

/// Image information
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct FileInfo {
    /// dimension: width x length of an image
    pub dimension: Dimension,
    /// Returns true if the dimension is valid
    pub is_valid: bool,
    /// The size of the file, in bytes
    pub size: u64,
    /// The image file path
    pub path: PathBuf,
}

impl FileInfo {
    /**
    Returns true if the given pattern matches a sub-slice of this path.

    Returns false if it does not.
    */
    pub fn path_contains(&self, string: &str) -> bool {
        match self.path.to_str() {
            Some(p) => p.contains(string),
            None => false,
        }
    }

    /// Update dimension field and is_valid field
    pub fn update_info(&mut self, config: &Config, args: &Arguments) -> MyResult<()> {
        // identify -format %wx%h image_file_path
        let mut cmd = Command::new("identify");
        let identify_cmd = cmd
            .arg("-format")
            .arg("%wx%h") // x separator
            .arg(&self.path);

        let identify_out = exec_cmd(identify_cmd, args, "identify")?;

        let sdt_output = String::from_utf8(identify_out.stdout)?;
        let dimension = Dimension::new(&sdt_output);

        self.dimension = dimension;
        self.is_valid = self.check_validity(config);

        // print FileInfo
        println!("{self:?}");

        Ok(())
    }

    /// Check if the dimension is valid
    fn check_validity(&self, config: &Config) -> bool {
        // Calculate the minimum between width and height
        // Calcular o mínimo entre largura e altura
        let width = self.dimension.width;
        let height = self.dimension.height;
        let min = width.min(height);

        min > config.dimension
    }
}

/// FileInfo Extension
pub trait FileInfoExt {
    fn get_width_min(&self) -> Option<u32>;
}

impl FileInfoExt for [FileInfo] {
    fn get_width_min(&self) -> Option<u32> {
        self.iter().map(|file_info| file_info.dimension.width).min()
    }
}

/// Dimension - width and length - of an image.
///
/// Image Size, Attribute, properties.
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct Dimension {
    pub width: u32,
    pub height: u32,
}

impl Dimension {
    pub fn new(string: &str) -> Dimension {
        let (width, height) = split_str(string);
        Dimension { width, height }
    }
}

fn split_str(string: &str) -> (u32, u32) {
    let numbers: Vec<u32> = string
        .trim()
        .split('x')
        .flat_map(|number| number.parse::<u32>())
        .collect();

    if numbers.len() != 2 {
        eprintln!("fn split_str()");
        panic!("Error: split '{string}' for Vec<u32>");
    }

    let width = numbers[0];
    let height = numbers[1];

    if numbers.iter().any(|&n| n == 0) {
        eprintln!("fn split_str()");
        eprintln!("width: {width}, heigth: {height}");
        panic!("width and height must be greater then zero!");
    }

    (width, height)
}

#[cfg(test)]
mod test_info {
    #[test]
    /// `cargo test -- --show-output get_min_value_of_vec`
    fn get_min_value_of_vec_v1() {
        let values: Vec<i32> = vec![5, 6, 8, 4, 2, 7];

        let min_value: Option<i32> = values.iter().min().copied();

        println!("values: {values:?}");
        println!("min_value: {min_value:?}");

        assert_eq!(min_value, Some(2));
    }

    #[test]
    /// `cargo test -- --show-output get_min_value_of_vec`
    ///
    /// <https://stackoverflow.com/questions/58669865/how-to-get-the-minimum-value-within-a-vector-in-rust>
    fn get_min_value_of_vec_v2() {
        let values: Vec<i32> = vec![5, 6, 8, 4, 2, 7];

        // The empty vector must be filtered beforehand!
        // let values: Vec<i32> = vec![]; // Not work!!!

        // Get the minimum value without being wrapped by Option<T>
        let min_value: i32 = values
            .iter()
            //.into_iter()
            //.fold(i32::MAX, i32::min);
            .fold(i32::MAX, |arg0: i32, other: &i32| i32::min(arg0, *other));

        println!("values: {values:?}");
        println!("min_value: {min_value}");

        assert_eq!(min_value, 2);
    }
}