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
use std::{
    fs::{self, File},
    io::{self, Read},
    path::PathBuf,
};

pub fn parse_file<T: Into<PathBuf>>(filepath: T) -> Result<syn::File, io::Error> {
    let pb: PathBuf = filepath.into();

    if !pb.is_file() {
        panic!("File not found: {:?}", pb);
    }

    let mut file = File::open(&pb)?;
    let mut content = String::new();
    file.read_to_string(&mut content)?;

    Ok(syn::parse_file(&content).unwrap_or_else(move |_| panic!("Failed to parse file {:?}", pb)))
}

/// Parse all the files in the given path
pub fn parse_files<T: Into<PathBuf>>(path: T) -> Result<Vec<(String, syn::File)>, io::Error> {
    let mut files: Vec<(String, syn::File)> = vec![];

    let pb: PathBuf = path.into();
    if pb.is_file() {
        // we only parse rust files
        if is_rust_file(&pb) {
            files.push((pb.to_str().unwrap().to_string(), parse_file(pb)?));
        }
    } else {
        for entry in fs::read_dir(pb)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_file() && is_rust_file(&path) {
                files.push((path.to_str().unwrap().to_string(), parse_file(path)?));
            } else {
                files.append(&mut parse_files(path)?);
            }
        }
    }
    Ok(files)
}

fn is_rust_file(path: &PathBuf) -> bool {
    path.is_file() && path.extension().unwrap().to_str().unwrap().eq("rs")
}

/// Extract the module name from the file path
/// # Example
/// ```
/// let module_name = extract_module_name_from_path(
///    &"./utoipa-auto-macro/tests/controllers/controller1.rs".to_string()
/// );
/// assert_eq!(
///  module_name,
/// "crate::tests::controllers::controller1".to_string()
/// );
/// ```
pub fn extract_module_name_from_path(path: &String) -> String {
    let mut path = path.to_string();
    if path.ends_with(".rs") {
        path = path.replace(".rs", "");
    }
    if path.ends_with("/mod") {
        path = path.replace("/mod", "");
    }
    if path.ends_with("/lib") {
        path = path.replace("/lib", "");
    }
    if path.ends_with("/main") {
        path = path.replace("/main", "");
    }
    path = path.replace("./", "");
    //remove first word
    let mut path_vec = path.split('/').collect::<Vec<&str>>();
    path_vec[0] = "crate";
    path_vec.join("::")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_extract_module_name_from_path() {
        assert_eq!(
            extract_module_name_from_path(
                &"./utoipa-auto-macro/tests/controllers/controller1.rs".to_string()
            ),
            "crate::tests::controllers::controller1"
        );
    }

    #[test]
    fn test_extract_module_name_from_mod() {
        assert_eq!(
            extract_module_name_from_path(
                &"./utoipa-auto-macro/tests/controllers/mod.rs".to_string()
            ),
            "crate::tests::controllers"
        );
    }

    #[test]
    fn test_extract_module_name_from_lib() {
        assert_eq!(
            extract_module_name_from_path(&"./src/lib.rs".to_string()),
            "crate"
        );
    }

    #[test]
    fn test_extract_module_name_from_main() {
        assert_eq!(
            extract_module_name_from_path(&"./src/main.rs".to_string()),
            "crate"
        );
    }
}