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
use anyhow::Result;
use console::style;
use glob::glob;
use rayon::prelude::*;
use std::{
    fs::File,
    path::Path,
    sync::{Arc, Mutex},
};

use crate::common::*;
use crate::utils::*;
use crate::validate::*;

pub struct ValidateArgs {
    pub assets_dir: String,
    pub strict: bool,
}

pub fn process_validate(args: ValidateArgs) -> Result<()> {
    // loading assets
    println!(
        "{} {}Loading assets",
        style("[1/1]").bold().dim(),
        ASSETS_EMOJI
    );

    let assets_dir = Path::new(&args.assets_dir);

    // missing or empty assets directory
    if !assets_dir.exists() || assets_dir.read_dir()?.next().is_none() {
        info!("Assets directory is missing or empty.");
        return Err(ValidateError::MissingOrEmptyAssetsDirectory.into());
    }

    let path = assets_dir.join("*.json");
    let pattern = path.to_str().ok_or(ValidateError::InvalidAssetsDirectory)?;

    let (paths, errors): (Vec<_>, Vec<_>) = glob(pattern)?.into_iter().partition(Result::is_ok);

    let pb = spinner_with_style();
    pb.enable_steady_tick(120);
    pb.set_message(format!("Validating {} metadata file(s)...", paths.len()));

    let paths: Vec<_> = paths.into_iter().map(Result::unwrap).collect();
    let path_errors: Vec<_> = errors.into_iter().map(Result::unwrap_err).collect();

    let file_open_errors = Arc::new(Mutex::new(Vec::new()));
    let deserialize_errors = Arc::new(Mutex::new(Vec::new()));
    let validate_errors = Arc::new(Mutex::new(Vec::new()));

    paths.par_iter().for_each(|path| {
        let file_open_errors = file_open_errors.clone();
        let f = match File::open(path) {
            Ok(f) => f,
            Err(error) => {
                error!("{}: {}", path.display(), error);
                file_open_errors
                    .lock()
                    .unwrap()
                    .push(FileOpenError { path, error });
                return;
            }
        };

        let metadata = match serde_json::from_reader::<File, Metadata>(f) {
            Ok(metadata) => metadata,
            Err(error) => {
                error!("{}: {}", path.display(), error);
                deserialize_errors
                    .lock()
                    .unwrap()
                    .push(DeserializeError { path, error });
                return;
            }
        };

        if args.strict {
            match metadata.validate_strict() {
                Ok(()) => {}
                Err(e) => {
                    error!("{}: {}", path.display(), e);
                    validate_errors.lock().unwrap().push(e);
                }
            }
        } else {
            match metadata.validate() {
                Ok(()) => {}
                Err(e) => {
                    error!("{}: {}", path.display(), e);
                    validate_errors.lock().unwrap().push(e);
                }
            }
        }
    });

    pb.finish();

    if !path_errors.is_empty() {
        error!("Path errors: {:?}", path_errors);
        return Err(ReadFilesError::PathErrors.into());
    }

    if !file_open_errors.lock().unwrap().is_empty() {
        error!("File open errors: {:?}", file_open_errors);
        return Err(ReadFilesError::FileOpenErrors.into());
    }

    if !deserialize_errors.lock().unwrap().is_empty() {
        error!("Deserialize errors: {:?}", deserialize_errors);
        return Err(ReadFilesError::DeserializeErrors.into());
    }

    if !validate_errors.lock().unwrap().is_empty() {
        error!("Validate errors: {:?}", validate_errors);
        return Err(ReadFilesError::ValidateErrors.into());
    }

    let message = "Validation complete, your metadata file(s) look good.";
    info!("{message}");
    println!("\n{message}");

    Ok(())
}