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
use hyper::{Body, Method, Request};
use std::fs;
use std::io;
use std::path::{Path, PathBuf};

use crate::{Dispatcher, Error, MultiFileIndexMap};
pub struct DefaultFileDispatcher {
    create_missing: bool,
    multifile_indices: MultiFileIndexMap,
}

impl DefaultFileDispatcher {
    pub fn new(create_missing: bool, index_map: MultiFileIndexMap) -> Self {
        Self {
            create_missing,
            multifile_indices: index_map,
        }
    }
}

impl Dispatcher for DefaultFileDispatcher {
    fn dispatch(
        &self,
        directory: impl AsRef<Path>,
        request: &Request<Body>,
    ) -> Result<PathBuf, Error> {
        let available_files = fs::read_dir(&directory)?
            .filter_map(Result::ok)
            .map(|entry| entry.path())
            .filter(|p| p.is_file())
            .filter(|p| file_matches(p, &request.method()))
            .collect::<Vec<PathBuf>>();

        if available_files.is_empty() {
            if self.create_missing {
                let file_name = format!("{}.json", request.method().to_string().to_lowercase());

                let mut path = PathBuf::new();
                path.push(directory);
                path.push(file_name);

                fs::File::create(&path)?;

                Ok(path)
            } else {
                Err(io::Error::new(io::ErrorKind::NotFound, "No files available").into())
            }
        } else {
            let mut indices = self.multifile_indices.lock().unwrap();
            let index = indices
                .entry(PathBuf::from(directory.as_ref()))
                .or_insert_with(|| 0);
            if *index >= available_files.len() {
                *index = 0;
            }

            match available_files.into_iter().nth(*index) {
                Some(file) => {
                    *index += 1;
                    Ok(file)
                }
                None => Err(io::Error::new(io::ErrorKind::Other, "Could not read file").into()),
            }
        }
    }
}

fn file_matches(file_path: &PathBuf, method: &Method) -> bool {
    let method_str = method.to_string().to_lowercase();

    match file_path.file_stem().and_then(|stem| stem.to_str()) {
        Some(stem) => {
            stem == method_str || stem.to_lowercase().starts_with(&format!("{}_", method_str))
        }
        None => false,
    }
}