Skip to main content

cratestack_parser/load/
sources.rs

1//! Where the loader gets file text from: the disk, or an editor's unsaved
2//! buffers laid over it (cratestack-lsp).
3
4use std::collections::HashMap;
5use std::io;
6use std::path::{Path, PathBuf};
7
8use super::paths::fold;
9
10/// Where file text comes from.
11pub trait SchemaSources {
12    fn read(&self, path: &Path) -> io::Result<String>;
13}
14
15/// The default: `std::fs::read_to_string`.
16#[derive(Debug, Clone, Copy, Default)]
17pub struct FsSources;
18
19impl SchemaSources for FsSources {
20    fn read(&self, path: &Path) -> io::Result<String> {
21        std::fs::read_to_string(path)
22    }
23}
24
25/// In-memory texts keyed by [`canonical_schema_path`], optionally falling
26/// back to the disk for a path that has none.
27#[derive(Debug, Clone)]
28pub struct OverlaySources {
29    files: HashMap<PathBuf, String>,
30    fallback_to_disk: bool,
31}
32
33impl OverlaySources {
34    pub fn new(fallback_to_disk: bool) -> Self {
35        Self {
36            files: HashMap::new(),
37            fallback_to_disk,
38        }
39    }
40
41    pub fn insert(&mut self, path: impl AsRef<Path>, text: impl Into<String>) {
42        self.files
43            .insert(canonical_schema_path(path.as_ref()), text.into());
44    }
45}
46
47impl SchemaSources for OverlaySources {
48    fn read(&self, path: &Path) -> io::Result<String> {
49        match self.files.get(&canonical_schema_path(path)) {
50            Some(text) => Ok(text.clone()),
51            None if self.fallback_to_disk => std::fs::read_to_string(path),
52            None => Err(io::Error::from(io::ErrorKind::NotFound)),
53        }
54    }
55}
56
57/// The identity of a schema file, so two spellings of one file are one file
58/// (docs/design/multi-file-schemas.md section 5, Q3): `std::fs::canonicalize`,
59/// or, for a file that does not exist, the absolute path with `.` and `..`
60/// folded away lexically.
61pub fn canonical_schema_path(path: &Path) -> PathBuf {
62    if let Ok(canonical) = std::fs::canonicalize(path) {
63        return canonical;
64    }
65    let absolute = std::path::absolute(path).unwrap_or_else(|_| path.to_path_buf());
66    fold(&absolute)
67}