schematic/validate/
extends.rs

1use crate::config::{ExtendsFrom, Path, PathSegment, ValidateError, ValidateResult};
2use crate::helpers::*;
3
4/// Validate an `extend` value is either a file path or secure URL.
5pub fn extends_string<D, C>(
6    value: &str,
7    _data: &D,
8    _context: &C,
9    _finalize: bool,
10) -> ValidateResult {
11    let is_file = is_file_like(value);
12    let is_url = is_url_like(value);
13
14    if !is_url && !is_file {
15        return Err(ValidateError::new(
16            "only file paths and URLs can be extended",
17        ));
18    }
19
20    if !value.is_empty() && !is_source_format(value) {
21        return Err(ValidateError::new(
22            "invalid file format, try a supported extension",
23        ));
24    }
25
26    if is_url && !is_secure_url(value) {
27        return Err(ValidateError::new("only secure URLs can be extended"));
28    }
29
30    Ok(())
31}
32
33/// Validate a list of `extend` values are either a file path or secure URL.
34pub fn extends_list<D, C>(
35    values: &[String],
36    data: &D,
37    context: &C,
38    finalize: bool,
39) -> ValidateResult {
40    for (i, value) in values.iter().enumerate() {
41        if let Err(mut error) = extends_string(value, data, context, finalize) {
42            error.path = Path::new(vec![PathSegment::Index(i)]);
43
44            return Err(error);
45        }
46    }
47
48    Ok(())
49}
50
51/// Validate an `extend` value is either a file path or secure URL.
52pub fn extends_from<D, C>(
53    value: &ExtendsFrom,
54    data: &D,
55    context: &C,
56    finalize: bool,
57) -> ValidateResult {
58    match value {
59        ExtendsFrom::String(string) => extends_string(string, data, context, finalize)?,
60        ExtendsFrom::List(list) => extends_list(list, data, context, finalize)?,
61    };
62
63    Ok(())
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn string_valid() {
72        assert!(extends_string("file.yml", &(), &(), false).is_ok());
73        assert!(extends_string("./file.json", &(), &(), false).is_ok());
74        assert!(extends_string("../file.yaml", &(), &(), false).is_ok());
75        assert!(extends_string("/nested/file.toml", &(), &(), false).is_ok());
76        assert!(extends_string("https://domain.com/file.yml", &(), &(), false).is_ok());
77        assert!(extends_string("https://domain.com/nested/file.toml", &(), &(), false).is_ok());
78        assert!(
79            extends_string(
80                "https://domain.com/nested/file.toml?query=string",
81                &(),
82                &(),
83                false
84            )
85            .is_ok()
86        );
87    }
88
89    #[test]
90    fn string_invalid() {
91        // invalid ext
92        assert!(extends_string("file.exe", &(), &(), false).is_err());
93        assert!(extends_string("https://domain.com/nested/file.exe", &(), &(), false).is_err());
94        assert!(
95            extends_string(
96                "https://domain.com/nested/file.exe?query=string",
97                &(),
98                &(),
99                false
100            )
101            .is_err()
102        );
103
104        // no http
105        assert!(extends_string("http://domain.com/nested/file.json", &(), &(), false).is_err());
106    }
107}