Skip to main content

diskann_benchmark_runner/
files.rs

1/*
2 * Copyright (c) Microsoft Corporation.
3 * Licensed under the MIT license.
4 */
5
6use std::path::{Path, PathBuf};
7
8use serde::{Deserialize, Serialize};
9
10use super::checker::{CheckDeserialization, Checker};
11
12/// A file that is used as an input to for a benchmark.
13///
14/// When used during input deserialization, with a [`Checker`], the following actions will be
15/// taken:
16///
17/// 1. If the path is absolute or points to an existing file relative to the current working
18///    directory, no additional measure will be taken.
19///
20/// 2. If the path is not absolute, then every directory in [`Checker::search_directories()`]
21///    will be explored in-order. The first directory where `path` exists will be selected.
22///
23/// 3. If all these steps fail, then post-deserialization checking will fail with an error.
24#[derive(Debug, Serialize, Deserialize, Clone)]
25#[serde(transparent)]
26pub struct InputFile {
27    path: PathBuf,
28}
29
30impl InputFile {
31    /// Create a new new input file from the path-like `path``
32    pub fn new<P>(path: P) -> Self
33    where
34        PathBuf: From<P>,
35    {
36        Self {
37            path: PathBuf::from(path),
38        }
39    }
40}
41
42impl std::ops::Deref for InputFile {
43    type Target = Path;
44    fn deref(&self) -> &Self::Target {
45        &self.path
46    }
47}
48
49impl CheckDeserialization for InputFile {
50    fn check_deserialization(&mut self, checker: &mut Checker) -> Result<(), anyhow::Error> {
51        let checked_path = checker.check_path(self);
52        match checked_path {
53            Ok(p) => {
54                self.path = p;
55                Ok(())
56            }
57            Err(e) => Err(e),
58        }
59    }
60}
61
62///////////
63// Tests //
64///////////
65
66#[cfg(test)]
67mod tests {
68    use std::fs::{create_dir, File};
69
70    use super::*;
71
72    #[test]
73    fn test_input_file() {
74        let file = InputFile::new("hello/world");
75        let file_deref: &Path = &file;
76        assert_eq!(file_deref.to_str().unwrap(), "hello/world");
77    }
78
79    #[test]
80    fn test_serialization() {
81        let st: &str = "\"path/to/directory\"";
82        let file: InputFile = serde_json::from_str(st).unwrap();
83        assert_eq!(file.to_str().unwrap(), st.trim_matches('\"'));
84
85        assert_eq!(&*serde_json::to_string(&file).unwrap(), st);
86    }
87
88    #[test]
89    fn test_check_deserialization() {
90        // We create a directory that looks like this:
91        //
92        // dir/
93        //     file_a.txt
94        //     dir0/
95        //        file_b.txt
96        //     dir1/
97        //        file_c.txt
98        //        dir0/
99        //           file_c.txt
100        let dir = tempfile::tempdir().unwrap();
101        let path = dir.path();
102
103        File::create(path.join("file_a.txt")).unwrap();
104        create_dir(path.join("dir0")).unwrap();
105        create_dir(path.join("dir1")).unwrap();
106        create_dir(path.join("dir1/dir0")).unwrap();
107        File::create(path.join("dir0/file_b.txt")).unwrap();
108        File::create(path.join("dir1/file_c.txt")).unwrap();
109        File::create(path.join("dir1/dir0/file_c.txt")).unwrap();
110
111        // Test absolute path success.
112        {
113            let absolute = path.join("file_a.txt");
114            let mut file = InputFile::new(absolute.clone());
115            let mut checker = Checker::new(Vec::new(), None);
116            file.check_deserialization(&mut checker).unwrap();
117            assert_eq!(file.path, absolute);
118
119            let absolute = path.join("dir0/file_b.txt");
120            let mut file = InputFile::new(absolute.clone());
121            let mut checker = Checker::new(Vec::new(), None);
122            file.check_deserialization(&mut checker).unwrap();
123            assert_eq!(file.path, absolute);
124        }
125
126        // Absolute path fail.
127        {
128            let absolute = path.join("dir0/file_c.txt");
129            let mut file = InputFile::new(absolute.clone());
130            let mut checker = Checker::new(Vec::new(), None);
131            let err = file.check_deserialization(&mut checker).unwrap_err();
132            let message = err.to_string();
133            assert!(message.contains("input file with absolute path"));
134            assert!(message.contains("either does not exist or is not a file"));
135        }
136
137        // Directory search
138        {
139            let mut checker = Checker::new(
140                vec![path.join("dir1/dir0"), path.join("dir1"), path.join("dir0")],
141                None,
142            );
143
144            // Directories are searched in order.
145            let mut file = InputFile::new("file_c.txt");
146            file.check_deserialization(&mut checker).unwrap();
147            assert_eq!(file.path, path.join("dir1/dir0/file_c.txt"));
148
149            let mut file = InputFile::new("file_b.txt");
150            file.check_deserialization(&mut checker).unwrap();
151            assert_eq!(file.path, path.join("dir0/file_b.txt"));
152
153            // Directory search can fail.
154            let mut file = InputFile::new("file_a.txt");
155            let err = file.check_deserialization(&mut checker).unwrap_err();
156            let message = err.to_string();
157            assert!(message.contains("could not find input file"));
158            assert!(message.contains("in the search directories"));
159
160            // If we give an absolute path, no directory search is performed.
161            let mut file = InputFile::new(path.join("file_c.txt"));
162            let err = file.check_deserialization(&mut checker).unwrap_err();
163            let message = err.to_string();
164            assert!(message.starts_with("input file with absolute path"));
165        }
166    }
167}