1 //! Paths for resources used by the application.
2 //!
3 //! Implemented as a trait to support extensibility and customizability, but
4 //! with a a set of framework conventions.
5 
6 pub use canonical_path::{CanonicalPath as AbsPath, CanonicalPathBuf as AbsPathBuf};
7 
8 // Just in case anyone gets confused why `Path` is private
9 pub use std::path::{Path, PathBuf};
10 
11 use crate::FrameworkError;
12 
13 /// Name of the application's secrets directory
14 pub(crate) const SECRETS_DIR: &str = "secrets";
15 
16 /// Path to the application's executable.
17 pub trait ExePath {
18     /// Get the path to the application's executable
exe(&self) -> &AbsPath19     fn exe(&self) -> &AbsPath;
20 }
21 
22 /// Path to application's root directory
23 pub trait RootPath {
24     /// Get the path to the application's root directory
root(&self) -> &AbsPath25     fn root(&self) -> &AbsPath;
26 }
27 
28 /// Path to the application's secrets directory
29 pub trait SecretsPath {
30     /// Get the path to the application's secrets directory
secrets(&self) -> &AbsPath31     fn secrets(&self) -> &AbsPath;
32 }
33 
34 /// Standard set of "happy paths" used by Abscissa applications.
35 ///
36 /// These are not yet finalized, but provide a standard application layout
37 /// (further expressed in the template) which future Abscissa
38 /// components/extensions should seek to adhere to.
39 #[derive(Clone, Debug)]
40 pub struct StandardPaths {
41     /// Path to the application's executable.
42     exe: AbsPathBuf,
43 
44     /// Path to the application's root directory
45     root: AbsPathBuf,
46 
47     /// Path to the application's secrets
48     secrets: Option<AbsPathBuf>,
49 }
50 
51 impl StandardPaths {
52     /// Compute paths to application resources from the path of the
53     /// application's executable:
54     ///
55     /// - `./` (root): application root directory
56     /// - `./{{~name~}}` (bin): application executable path
57     /// - `./secrets` (secrets): location of files containing app's secrets
from_exe_path<P>(exe_path: P) -> Result<Self, FrameworkError> where P: Into<AbsPathBuf>,58     fn from_exe_path<P>(exe_path: P) -> Result<Self, FrameworkError>
59     where
60         P: Into<AbsPathBuf>,
61     {
62         let exe = exe_path.into();
63         let root = exe.parent()?;
64         let secrets = root.join(SECRETS_DIR).ok();
65         Ok(StandardPaths { exe, root, secrets })
66     }
67 }
68 
69 impl Default for StandardPaths {
70     // TODO(tarcieri): better error handling for canonicalization failures
default() -> Self71     fn default() -> Self {
72         let exe_path = canonical_path::current_exe().unwrap_or_else(|e| {
73             panic!("error canonicalizing application path: {}", e);
74         });
75 
76         Self::from_exe_path(exe_path).unwrap_or_else(|e| {
77             panic!("error computing application paths: {}", e);
78         })
79     }
80 }
81 
82 impl ExePath for StandardPaths {
exe(&self) -> &AbsPath83     fn exe(&self) -> &AbsPath {
84         self.exe.as_ref()
85     }
86 }
87 
88 impl RootPath for StandardPaths {
root(&self) -> &AbsPath89     fn root(&self) -> &AbsPath {
90         self.root.as_ref()
91     }
92 }
93 
94 impl SecretsPath for StandardPaths {
secrets(&self) -> &AbsPath95     fn secrets(&self) -> &AbsPath {
96         self.secrets
97             .as_ref()
98             .unwrap_or_else(|| {
99                 // TODO(tarcieri): better error handling for this case
100                 panic!("secrets directory does not exist");
101             })
102             .as_ref()
103     }
104 }
105