stubr 0.6.2

Wiremock implemented in Rust
Documentation
use std::fs::read_dir;
use std::{
    env,
    ffi::OsStr,
    path::{Path, PathBuf},
};

use itertools::Itertools;

pub struct StubFinder;

impl StubFinder {
    const LOCAL_DIR: &'static str = "stubr";
    const JSON_EXTENSION: &'static str = "json";

    #[cfg(target_os = "macos")]
    const LIB_PATH_ENV_VAR: &'static str = "DYLD_FALLBACK_LIBRARY_PATH";
    #[cfg(target_os = "windows")]
    const LIB_PATH_ENV_VAR: &'static str = "PATH";
    #[cfg(target_os = "linux")]
    const LIB_PATH_ENV_VAR: &'static str = "LD_LIBRARY_PATH";

    pub fn find_all_stubs(from: &Path) -> impl Iterator<Item = PathBuf> {
        if from.exists() {
            if from.is_dir() {
                Self::find_all_stubs_under_dir(from).into_iter()
            } else {
                vec![from.to_path_buf()].into_iter()
            }
        } else {
            vec![].into_iter()
        }
    }

    fn find_all_stubs_under_dir(from: &Path) -> Vec<PathBuf> {
        let mut stubs = vec![];
        if let Ok(mut from) = read_dir(from) {
            while let Some(Ok(entry)) = from.next() {
                let path = entry.path();
                if path.is_file() && path.extension().and_then(OsStr::to_str) == Some(Self::JSON_EXTENSION) {
                    stubs.push(path)
                } else if path.is_dir() {
                    Self::find_all_stubs_under_dir(&path).into_iter().for_each(|s| stubs.push(s))
                }
            }
        }
        stubs
    }

    pub fn find_app(name: &str) -> PathBuf {
        env::var("CARGO_PKG_NAME")
            .ok()
            .map(|pkg| Self::output_dir().join(Self::LOCAL_DIR).join(pkg).join(name))
            .filter(|path| path.exists())
            .unwrap_or_else(|| panic!("Could not find app '{name}'"))
    }

    pub fn output_dir() -> PathBuf {
        env::var(Self::LIB_PATH_ENV_VAR)
            .ok()
            .and_then(|v| v.split(':').map(PathBuf::from).find(|p| Self::is_target_debug(p)))
            .and_then(|p| p.parent().map(|it| it.to_path_buf()))
            .expect("Failed locating '/target' directory")
    }

    fn is_target_debug(path: &Path) -> bool {
        let is_named = |p: &Path, name: &str| {
            p.file_name()
                .and_then(OsStr::to_str)
                .map(|n| n.split(';').collect_vec())
                .and_then(|v| v.first().map(|it| it.to_string()))
                == Some(name.to_string())
        };
        let debug = is_named(path, "debug");
        let target = path.parent().map(|p| is_named(p, "target")).unwrap_or_default();
        debug && target
    }
}

#[cfg(test)]
mod stub_finder_test {
    use itertools::Itertools;

    use super::*;

    #[test]
    fn should_find_just_json_files_from_dir() {
        let from = PathBuf::from("tests/stubs/server");
        let files = StubFinder::find_all_stubs(&from).collect::<Vec<PathBuf>>();
        assert!(files.len().gt(&2));
        let file_names = files.iter().map(|it| it.file_name().unwrap().to_str().unwrap()).collect_vec();
        assert!(file_names.contains(&"valid.json"));
        assert!(file_names.contains(&"also_valid.json"));
    }

    #[test]
    fn should_find_all_files_from_single_file() {
        let from = PathBuf::from("tests/stubs/server/valid.json");
        let files = StubFinder::find_all_stubs(&from).collect::<Vec<PathBuf>>();
        assert_eq!(files.len(), 1);
        let file_names = files.iter().map(|it| it.file_name().unwrap().to_str().unwrap()).collect_vec();
        assert!(file_names.contains(&"valid.json"));
    }

    #[test]
    fn should_not_find_any_file_when_path_does_not_exist() {
        let from = PathBuf::from("tests/stubs/server/unknown");
        assert_eq!(StubFinder::find_all_stubs(&from).count(), 0);
        let from = PathBuf::from("tests/stubs/server/unknown.json");
        assert_eq!(StubFinder::find_all_stubs(&from).count(), 0);
    }

    #[test]
    fn should_return_empty_vec_when_read_dir_fails() {
        let from = PathBuf::from("tests/stubs/server/unknown.json");
        let files = StubFinder::find_all_stubs_under_dir(&from);
        assert!(files.is_empty());
    }

    #[test]
    fn should_find_all_stubs_recursively() {
        let from = PathBuf::from("tests/stubs/recur");
        let files = StubFinder::find_all_stubs(&from).collect::<Vec<PathBuf>>();
        assert_eq!(files.len(), 3);
        let file_names = files.iter().map(|it| it.file_name().unwrap().to_str().unwrap()).collect_vec();
        assert!(file_names.contains(&"get.json"));
        assert!(file_names.contains(&"post.json"));
        assert!(file_names.contains(&"delete.json"));
    }
}