conventions.rs (11205B)
1 use std::path::{Path, PathBuf}; 2 3 use crate::{ 4 RadrootsPathOverrides, RadrootsPathProfile, RadrootsPathResolver, RadrootsRuntimeNamespace, 5 RadrootsRuntimePathsError, 6 }; 7 8 pub const DEFAULT_CONFIG_FILE_NAME: &str = "config.toml"; 9 pub const DEFAULT_SERVICE_IDENTITY_FILE_NAME: &str = "identity.secret.json"; 10 pub const DEFAULT_SHARED_IDENTITY_FILE_NAME: &str = "default.json"; 11 pub const DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE_KIND: &str = "shared"; 12 pub const DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE_VALUE: &str = "local_events"; 13 pub const DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE: &str = "shared/local_events"; 14 pub const DEFAULT_SHARED_LOCAL_EVENTS_DB_FILE_NAME: &str = "local_events.sqlite"; 15 16 #[derive(Debug, Clone, PartialEq, Eq)] 17 pub struct RadrootsBootstrapPaths { 18 pub config_path: PathBuf, 19 pub logs_dir: PathBuf, 20 pub identity_path: PathBuf, 21 } 22 23 pub fn default_namespaced_bootstrap_paths( 24 resolver: &RadrootsPathResolver, 25 profile: RadrootsPathProfile, 26 overrides: &RadrootsPathOverrides, 27 namespace: &RadrootsRuntimeNamespace, 28 identity_file_name: &str, 29 ) -> Result<RadrootsBootstrapPaths, RadrootsRuntimePathsError> { 30 let namespaced = resolver.resolve(profile, overrides)?.namespaced(namespace); 31 Ok(RadrootsBootstrapPaths { 32 config_path: namespaced.config.join(DEFAULT_CONFIG_FILE_NAME), 33 logs_dir: namespaced.logs, 34 identity_path: namespaced.secrets.join(identity_file_name), 35 }) 36 } 37 38 pub fn default_shared_identity_path( 39 resolver: &RadrootsPathResolver, 40 profile: RadrootsPathProfile, 41 overrides: &RadrootsPathOverrides, 42 ) -> Result<PathBuf, RadrootsRuntimePathsError> { 43 let namespace = RadrootsRuntimeNamespace::shared("identities")?; 44 let namespaced = resolver.resolve(profile, overrides)?.namespaced(&namespace); 45 Ok(namespaced.secrets.join(DEFAULT_SHARED_IDENTITY_FILE_NAME)) 46 } 47 48 pub fn default_shared_runtime_logs_dir( 49 resolver: &RadrootsPathResolver, 50 profile: RadrootsPathProfile, 51 overrides: &RadrootsPathOverrides, 52 ) -> Result<PathBuf, RadrootsRuntimePathsError> { 53 let namespace = RadrootsRuntimeNamespace::shared("runtime")?; 54 let namespaced = resolver.resolve(profile, overrides)?.namespaced(&namespace); 55 Ok(namespaced.logs) 56 } 57 58 #[must_use] 59 pub fn default_shared_local_events_root_from_data_root(data_root: impl AsRef<Path>) -> PathBuf { 60 data_root 61 .as_ref() 62 .join(DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE_KIND) 63 .join(DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE_VALUE) 64 } 65 66 #[must_use] 67 pub fn default_shared_local_events_database_path_from_data_root( 68 data_root: impl AsRef<Path>, 69 ) -> PathBuf { 70 default_shared_local_events_root_from_data_root(data_root) 71 .join(DEFAULT_SHARED_LOCAL_EVENTS_DB_FILE_NAME) 72 } 73 74 pub fn default_shared_local_events_root_from_shared_accounts_data_root( 75 shared_accounts_data_root: impl AsRef<Path>, 76 ) -> Result<PathBuf, RadrootsRuntimePathsError> { 77 let shared_accounts_data_root = shared_accounts_data_root.as_ref(); 78 let shared_data_root = shared_accounts_data_root.parent().ok_or_else(|| { 79 RadrootsRuntimePathsError::SharedAccountsDataRootMissingParent { 80 path: shared_accounts_data_root.to_path_buf(), 81 } 82 })?; 83 Ok(shared_data_root.join(DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE_VALUE)) 84 } 85 86 pub fn default_shared_local_events_database_path_from_shared_accounts_data_root( 87 shared_accounts_data_root: impl AsRef<Path>, 88 ) -> Result<PathBuf, RadrootsRuntimePathsError> { 89 default_shared_local_events_root_from_shared_accounts_data_root(shared_accounts_data_root) 90 .map(|root| root.join(DEFAULT_SHARED_LOCAL_EVENTS_DB_FILE_NAME)) 91 } 92 93 #[cfg(test)] 94 mod tests { 95 use std::path::PathBuf; 96 97 use crate::{ 98 RadrootsHostEnvironment, RadrootsPlatform, RadrootsRuntimeNamespace, 99 RadrootsRuntimePathsError, 100 }; 101 102 use super::{ 103 DEFAULT_SERVICE_IDENTITY_FILE_NAME, DEFAULT_SHARED_IDENTITY_FILE_NAME, 104 DEFAULT_SHARED_LOCAL_EVENTS_DB_FILE_NAME, DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE, 105 default_namespaced_bootstrap_paths, default_shared_identity_path, 106 default_shared_local_events_database_path_from_data_root, 107 default_shared_local_events_database_path_from_shared_accounts_data_root, 108 default_shared_local_events_root_from_data_root, 109 default_shared_local_events_root_from_shared_accounts_data_root, 110 default_shared_runtime_logs_dir, 111 }; 112 113 #[test] 114 fn namespaced_bootstrap_paths_use_canonical_interactive_roots() { 115 let resolver = crate::RadrootsPathResolver::new( 116 RadrootsPlatform::Linux, 117 RadrootsHostEnvironment { 118 home_dir: Some(PathBuf::from("/home/treesap")), 119 ..RadrootsHostEnvironment::default() 120 }, 121 ); 122 let namespace = 123 RadrootsRuntimeNamespace::service("radrootsd").expect("service namespace should parse"); 124 125 let paths = default_namespaced_bootstrap_paths( 126 &resolver, 127 crate::RadrootsPathProfile::InteractiveUser, 128 &crate::RadrootsPathOverrides::default(), 129 &namespace, 130 DEFAULT_SERVICE_IDENTITY_FILE_NAME, 131 ) 132 .expect("service bootstrap paths should resolve"); 133 134 assert_eq!( 135 paths.config_path, 136 PathBuf::from("/home/treesap/.radroots/config/services/radrootsd/config.toml") 137 ); 138 assert_eq!( 139 paths.logs_dir, 140 PathBuf::from("/home/treesap/.radroots/logs/services/radrootsd") 141 ); 142 assert_eq!( 143 paths.identity_path, 144 PathBuf::from( 145 "/home/treesap/.radroots/secrets/services/radrootsd/identity.secret.json" 146 ) 147 ); 148 } 149 150 #[test] 151 fn shared_defaults_use_shared_namespaces() { 152 let resolver = crate::RadrootsPathResolver::new( 153 RadrootsPlatform::Macos, 154 RadrootsHostEnvironment { 155 home_dir: Some(PathBuf::from("/Users/treesap")), 156 ..RadrootsHostEnvironment::default() 157 }, 158 ); 159 160 let identity_path = default_shared_identity_path( 161 &resolver, 162 crate::RadrootsPathProfile::InteractiveUser, 163 &crate::RadrootsPathOverrides::default(), 164 ) 165 .expect("shared identity path should resolve"); 166 assert_eq!( 167 identity_path, 168 PathBuf::from("/Users/treesap/.radroots/secrets/shared/identities") 169 .join(DEFAULT_SHARED_IDENTITY_FILE_NAME) 170 ); 171 172 let logs_dir = default_shared_runtime_logs_dir( 173 &resolver, 174 crate::RadrootsPathProfile::InteractiveUser, 175 &crate::RadrootsPathOverrides::default(), 176 ) 177 .expect("shared runtime logs dir should resolve"); 178 assert_eq!( 179 logs_dir, 180 PathBuf::from("/Users/treesap/.radroots/logs/shared/runtime") 181 ); 182 } 183 184 #[test] 185 fn shared_local_events_paths_use_canonical_shared_namespace() { 186 let data_root = PathBuf::from("/repo/infra/local/runtime/radroots/data"); 187 188 assert_eq!( 189 default_shared_local_events_root_from_data_root(&data_root), 190 data_root.join(DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE) 191 ); 192 assert_eq!( 193 default_shared_local_events_database_path_from_data_root(&data_root), 194 data_root 195 .join(DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE) 196 .join(DEFAULT_SHARED_LOCAL_EVENTS_DB_FILE_NAME) 197 ); 198 } 199 200 #[test] 201 fn shared_local_events_paths_derive_from_shared_accounts_data_root() { 202 let shared_accounts_data_root = 203 PathBuf::from("/repo/infra/local/runtime/radroots/data/shared/accounts"); 204 205 assert_eq!( 206 default_shared_local_events_root_from_shared_accounts_data_root( 207 &shared_accounts_data_root 208 ) 209 .expect("shared local-events root"), 210 PathBuf::from("/repo/infra/local/runtime/radroots/data/shared/local_events") 211 ); 212 assert_eq!( 213 default_shared_local_events_root_from_shared_accounts_data_root( 214 shared_accounts_data_root.clone() 215 ) 216 .expect("shared local-events root from owned path"), 217 PathBuf::from("/repo/infra/local/runtime/radroots/data/shared/local_events") 218 ); 219 assert_eq!( 220 default_shared_local_events_database_path_from_shared_accounts_data_root( 221 &shared_accounts_data_root 222 ) 223 .expect("shared local-events database path"), 224 PathBuf::from( 225 "/repo/infra/local/runtime/radroots/data/shared/local_events/local_events.sqlite" 226 ) 227 ); 228 229 let err = 230 default_shared_local_events_root_from_shared_accounts_data_root(PathBuf::from("/")) 231 .expect_err("root path has no parent shared data root"); 232 assert_eq!( 233 err, 234 RadrootsRuntimePathsError::SharedAccountsDataRootMissingParent { 235 path: PathBuf::from("/") 236 } 237 ); 238 } 239 240 #[test] 241 fn namespaced_bootstrap_paths_propagate_resolver_errors() { 242 let resolver = 243 crate::RadrootsPathResolver::new(RadrootsPlatform::Linux, Default::default()); 244 let namespace = 245 RadrootsRuntimeNamespace::service("radrootsd").expect("service namespace should parse"); 246 247 let err = default_namespaced_bootstrap_paths( 248 &resolver, 249 crate::RadrootsPathProfile::InteractiveUser, 250 &crate::RadrootsPathOverrides::default(), 251 &namespace, 252 DEFAULT_SERVICE_IDENTITY_FILE_NAME, 253 ) 254 .expect_err("interactive user should require a home dir"); 255 256 assert_eq!( 257 err, 258 crate::RadrootsRuntimePathsError::MissingHomeDir { 259 platform: RadrootsPlatform::Linux, 260 } 261 ); 262 } 263 264 #[test] 265 fn shared_defaults_propagate_profile_errors() { 266 let resolver = 267 crate::RadrootsPathResolver::new(RadrootsPlatform::Android, Default::default()); 268 269 let identity_err = default_shared_identity_path( 270 &resolver, 271 crate::RadrootsPathProfile::InteractiveUser, 272 &crate::RadrootsPathOverrides::default(), 273 ) 274 .expect_err("interactive_user should be unsupported on android"); 275 assert_eq!( 276 identity_err, 277 crate::RadrootsRuntimePathsError::UnsupportedProfilePlatform { 278 profile: crate::RadrootsPathProfile::InteractiveUser, 279 platform: RadrootsPlatform::Android, 280 } 281 ); 282 283 let logs_err = default_shared_runtime_logs_dir( 284 &resolver, 285 crate::RadrootsPathProfile::ServiceHost, 286 &crate::RadrootsPathOverrides::default(), 287 ) 288 .expect_err("service_host should be unsupported on android"); 289 assert_eq!( 290 logs_err, 291 crate::RadrootsRuntimePathsError::UnsupportedProfilePlatform { 292 profile: crate::RadrootsPathProfile::ServiceHost, 293 platform: RadrootsPlatform::Android, 294 } 295 ); 296 } 297 }