1use std::fmt;
2use std::fs;
3use std::io;
4use std::path::{Path, PathBuf};
5
6#[derive(Debug)]
8pub struct FilesystemProject {
9 source: String,
10 main_path: PathBuf,
11 root: PathBuf,
12}
13
14impl FilesystemProject {
15 pub fn load(path: &Path) -> Result<Self, FilesystemProjectError> {
17 let canonical_path = path
18 .canonicalize()
19 .map_err(|error| FilesystemProjectError::entry_not_found(path.to_path_buf(), error))?;
20 let source = fs::read_to_string(&canonical_path).map_err(|error| {
21 FilesystemProjectError::entry_unreadable(canonical_path.clone(), error)
22 })?;
23 let root = canonical_path
24 .parent()
25 .map(Path::to_path_buf)
26 .unwrap_or_else(|| PathBuf::from("."));
27 Ok(Self {
28 source,
29 main_path: path.to_path_buf(),
30 root,
31 })
32 }
33
34 pub fn source(&self) -> &str {
36 &self.source
37 }
38
39 pub fn main_path(&self) -> &Path {
41 &self.main_path
42 }
43
44 pub fn root(&self) -> &Path {
46 &self.root
47 }
48}
49
50#[derive(Debug)]
52pub enum FilesystemProjectError {
53 EntryNotFound { path: PathBuf, source: io::Error },
54 EntryUnreadable { path: PathBuf, source: io::Error },
55}
56
57impl FilesystemProjectError {
58 fn entry_not_found(path: PathBuf, source: io::Error) -> Self {
59 Self::EntryNotFound { path, source }
60 }
61
62 fn entry_unreadable(path: PathBuf, source: io::Error) -> Self {
63 Self::EntryUnreadable { path, source }
64 }
65
66 pub fn is_entry_not_found(&self) -> bool {
68 matches!(self, Self::EntryNotFound { .. })
69 }
70}
71
72impl fmt::Display for FilesystemProjectError {
73 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
74 match self {
75 Self::EntryNotFound { source, .. } => write!(formatter, "cannot find entry: {source}"),
76 Self::EntryUnreadable { source, .. } => {
77 write!(formatter, "cannot read entry: {source}")
78 }
79 }
80 }
81}
82
83impl std::error::Error for FilesystemProjectError {
84 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
85 match self {
86 Self::EntryNotFound { source, .. } | Self::EntryUnreadable { source, .. } => {
87 Some(source)
88 }
89 }
90 }
91}