protox/file/
chain.rs

1use std::{fmt, path::Path};
2
3use super::{File, FileResolver};
4use crate::Error;
5
6/// An implementation of [`FileResolver`] which chains together several other resolvers.
7///
8/// When opening files, each resolver is searched in turn until the file is found.
9#[derive(Default)]
10pub struct ChainFileResolver {
11    resolvers: Vec<Box<dyn FileResolver>>,
12}
13
14impl ChainFileResolver {
15    /// Creates a new, empty [`ChainFileResolver`].
16    pub fn new() -> Self {
17        Default::default()
18    }
19
20    /// Adds a new resolver.
21    ///
22    /// The new resolver will be searched after all previously-added resolvers.
23    pub fn add<F>(&mut self, resolver: F)
24    where
25        F: FileResolver + 'static,
26    {
27        self.resolvers.push(Box::new(resolver))
28    }
29}
30
31impl FileResolver for ChainFileResolver {
32    fn resolve_path(&self, path: &Path) -> Option<String> {
33        for resolver in &self.resolvers {
34            if let Some(name) = resolver.resolve_path(path) {
35                return Some(name);
36            }
37        }
38
39        None
40    }
41
42    fn open_file(&self, name: &str) -> Result<File, Error> {
43        for resolver in &self.resolvers {
44            match resolver.open_file(name) {
45                Ok(file) => return Ok(file),
46                Err(err) if err.is_file_not_found() => continue,
47                Err(err) => return Err(err),
48            }
49        }
50
51        Err(Error::file_not_found(name))
52    }
53}
54
55impl fmt::Debug for ChainFileResolver {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_struct("ChainFileResolver").finish_non_exhaustive()
58    }
59}