lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

lib.rs (11945B)


      1 #![forbid(unsafe_code)]
      2 
      3 pub mod conventions;
      4 pub mod error;
      5 pub mod migration;
      6 pub mod namespace;
      7 pub mod platform;
      8 pub mod roots;
      9 pub mod service;
     10 
     11 pub use conventions::{
     12     DEFAULT_CONFIG_FILE_NAME, DEFAULT_SERVICE_IDENTITY_FILE_NAME,
     13     DEFAULT_SHARED_IDENTITY_FILE_NAME, DEFAULT_SHARED_LOCAL_EVENTS_DB_FILE_NAME,
     14     DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE, DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE_KIND,
     15     DEFAULT_SHARED_LOCAL_EVENTS_NAMESPACE_VALUE, RadrootsBootstrapPaths,
     16     default_namespaced_bootstrap_paths, default_shared_identity_path,
     17     default_shared_local_events_database_path_from_data_root,
     18     default_shared_local_events_database_path_from_shared_accounts_data_root,
     19     default_shared_local_events_root_from_data_root,
     20     default_shared_local_events_root_from_shared_accounts_data_root,
     21     default_shared_runtime_logs_dir,
     22 };
     23 pub use error::RadrootsRuntimePathsError;
     24 pub use migration::{
     25     RADROOTS_MIGRATION_COMPATIBILITY_WINDOW, RADROOTS_MIGRATION_POSTURE,
     26     RadrootsLegacyPathCandidate, RadrootsLegacyPathDetection, RadrootsMigrationReport,
     27     inspect_legacy_paths,
     28 };
     29 pub use namespace::{RadrootsRuntimeNamespace, RadrootsRuntimeNamespaceKind};
     30 pub use platform::{RadrootsHostEnvironment, RadrootsPathProfile, RadrootsPlatform};
     31 pub use roots::{RadrootsPathOverrides, RadrootsPathResolver, RadrootsPaths};
     32 pub use service::{
     33     RadrootsRuntimeLegacyPathContract, RadrootsRuntimeMigrationContract,
     34     RadrootsRuntimePathConfigEntry, RadrootsRuntimePathPolicyContract,
     35     RadrootsRuntimePathSelection, RadrootsRuntimePathSelectionError,
     36     RadrootsRuntimeSelectionContract, RadrootsRuntimeSelectionOverrideContract,
     37     runtime_migration_contract,
     38 };
     39 
     40 #[cfg(test)]
     41 mod tests {
     42     use std::path::PathBuf;
     43 
     44     use super::{
     45         RadrootsHostEnvironment, RadrootsPathOverrides, RadrootsPathProfile, RadrootsPathResolver,
     46         RadrootsPaths, RadrootsPlatform, RadrootsRuntimeNamespace, RadrootsRuntimePathsError,
     47     };
     48 
     49     #[test]
     50     fn interactive_user_linux_uses_home_dotradroots_root() {
     51         let resolver = RadrootsPathResolver::new(
     52             RadrootsPlatform::Linux,
     53             RadrootsHostEnvironment {
     54                 home_dir: Some(PathBuf::from("/home/treesap")),
     55                 ..RadrootsHostEnvironment::default()
     56             },
     57         );
     58 
     59         let roots = resolver
     60             .resolve(
     61                 RadrootsPathProfile::InteractiveUser,
     62                 &RadrootsPathOverrides::default(),
     63             )
     64             .expect("resolve linux interactive roots");
     65 
     66         assert_eq!(
     67             roots,
     68             RadrootsPaths::from_base_root("/home/treesap/.radroots")
     69         );
     70     }
     71 
     72     #[test]
     73     fn interactive_user_macos_uses_home_dotradroots_root() {
     74         let resolver = RadrootsPathResolver::new(
     75             RadrootsPlatform::Macos,
     76             RadrootsHostEnvironment {
     77                 home_dir: Some(PathBuf::from("/Users/treesap")),
     78                 ..RadrootsHostEnvironment::default()
     79             },
     80         );
     81 
     82         let roots = resolver
     83             .resolve(
     84                 RadrootsPathProfile::InteractiveUser,
     85                 &RadrootsPathOverrides::default(),
     86             )
     87             .expect("resolve macos interactive roots");
     88 
     89         assert_eq!(
     90             roots,
     91             RadrootsPaths::from_base_root("/Users/treesap/.radroots")
     92         );
     93     }
     94 
     95     #[test]
     96     fn interactive_user_windows_uses_native_user_roots() {
     97         let resolver = RadrootsPathResolver::new(
     98             RadrootsPlatform::Windows,
     99             RadrootsHostEnvironment {
    100                 appdata_dir: Some(PathBuf::from(r"C:\Users\treesap\AppData\Roaming")),
    101                 localappdata_dir: Some(PathBuf::from(r"C:\Users\treesap\AppData\Local")),
    102                 ..RadrootsHostEnvironment::default()
    103             },
    104         );
    105 
    106         let roots = resolver
    107             .resolve(
    108                 RadrootsPathProfile::InteractiveUser,
    109                 &RadrootsPathOverrides::default(),
    110             )
    111             .expect("resolve windows interactive roots");
    112 
    113         assert_eq!(
    114             roots,
    115             RadrootsPaths {
    116                 config: PathBuf::from(r"C:\Users\treesap\AppData\Roaming")
    117                     .join("Radroots")
    118                     .join("config"),
    119                 data: PathBuf::from(r"C:\Users\treesap\AppData\Local")
    120                     .join("Radroots")
    121                     .join("data"),
    122                 cache: PathBuf::from(r"C:\Users\treesap\AppData\Local")
    123                     .join("Radroots")
    124                     .join("cache"),
    125                 logs: PathBuf::from(r"C:\Users\treesap\AppData\Local")
    126                     .join("Radroots")
    127                     .join("logs"),
    128                 run: PathBuf::from(r"C:\Users\treesap\AppData\Local")
    129                     .join("Radroots")
    130                     .join("run"),
    131                 secrets: PathBuf::from(r"C:\Users\treesap\AppData\Roaming")
    132                     .join("Radroots")
    133                     .join("secrets"),
    134             }
    135         );
    136     }
    137 
    138     #[test]
    139     fn service_host_unix_uses_canonical_service_roots() {
    140         let resolver =
    141             RadrootsPathResolver::new(RadrootsPlatform::Linux, RadrootsHostEnvironment::default());
    142 
    143         let roots = resolver
    144             .resolve(
    145                 RadrootsPathProfile::ServiceHost,
    146                 &RadrootsPathOverrides::default(),
    147             )
    148             .expect("resolve service_host roots");
    149 
    150         assert_eq!(
    151             roots,
    152             RadrootsPaths {
    153                 config: PathBuf::from("/etc/radroots"),
    154                 data: PathBuf::from("/var/lib/radroots"),
    155                 cache: PathBuf::from("/var/cache/radroots"),
    156                 logs: PathBuf::from("/var/log/radroots"),
    157                 run: PathBuf::from("/run/radroots"),
    158                 secrets: PathBuf::from("/etc/radroots/secrets"),
    159             }
    160         );
    161     }
    162 
    163     #[test]
    164     fn service_host_windows_uses_programdata_roots() {
    165         let resolver = RadrootsPathResolver::new(
    166             RadrootsPlatform::Windows,
    167             RadrootsHostEnvironment {
    168                 programdata_dir: Some(PathBuf::from(r"C:\ProgramData")),
    169                 ..RadrootsHostEnvironment::default()
    170             },
    171         );
    172 
    173         let roots = resolver
    174             .resolve(
    175                 RadrootsPathProfile::ServiceHost,
    176                 &RadrootsPathOverrides::default(),
    177             )
    178             .expect("resolve service_host roots");
    179 
    180         assert_eq!(
    181             roots,
    182             RadrootsPaths {
    183                 config: PathBuf::from(r"C:\ProgramData")
    184                     .join("Radroots")
    185                     .join("config"),
    186                 data: PathBuf::from(r"C:\ProgramData")
    187                     .join("Radroots")
    188                     .join("data"),
    189                 cache: PathBuf::from(r"C:\ProgramData")
    190                     .join("Radroots")
    191                     .join("cache"),
    192                 logs: PathBuf::from(r"C:\ProgramData")
    193                     .join("Radroots")
    194                     .join("logs"),
    195                 run: PathBuf::from(r"C:\ProgramData")
    196                     .join("Radroots")
    197                     .join("run"),
    198                 secrets: PathBuf::from(r"C:\ProgramData")
    199                     .join("Radroots")
    200                     .join("secrets"),
    201             }
    202         );
    203     }
    204 
    205     #[test]
    206     fn repo_local_requires_explicit_base_root() {
    207         let resolver =
    208             RadrootsPathResolver::new(RadrootsPlatform::Linux, RadrootsHostEnvironment::default());
    209 
    210         let err = resolver
    211             .resolve(
    212                 RadrootsPathProfile::RepoLocal,
    213                 &RadrootsPathOverrides::default(),
    214             )
    215             .expect_err("repo_local should require an explicit base root");
    216 
    217         assert_eq!(err, RadrootsRuntimePathsError::MissingRepoLocalRoot);
    218     }
    219 
    220     #[test]
    221     fn repo_local_uses_explicit_base_root() {
    222         let resolver =
    223             RadrootsPathResolver::new(RadrootsPlatform::Linux, RadrootsHostEnvironment::default());
    224 
    225         let roots = resolver
    226             .resolve(
    227                 RadrootsPathProfile::RepoLocal,
    228                 &RadrootsPathOverrides::repo_local("/repo/.local/radroots"),
    229             )
    230             .expect("resolve repo_local roots");
    231 
    232         assert_eq!(
    233             roots,
    234             RadrootsPaths::from_base_root("/repo/.local/radroots")
    235         );
    236     }
    237 
    238     #[test]
    239     fn mobile_native_requires_explicit_roots() {
    240         let resolver = RadrootsPathResolver::new(
    241             RadrootsPlatform::Android,
    242             RadrootsHostEnvironment::default(),
    243         );
    244 
    245         let err = resolver
    246             .resolve(
    247                 RadrootsPathProfile::MobileNative,
    248                 &RadrootsPathOverrides::default(),
    249             )
    250             .expect_err("mobile_native should require explicit roots");
    251 
    252         assert_eq!(err, RadrootsRuntimePathsError::MissingMobileRoots);
    253     }
    254 
    255     #[test]
    256     fn mobile_native_returns_explicit_roots() {
    257         let resolver =
    258             RadrootsPathResolver::new(RadrootsPlatform::Ios, RadrootsHostEnvironment::default());
    259         let mobile_roots = RadrootsPaths {
    260             config: PathBuf::from("/sandbox/config"),
    261             data: PathBuf::from("/sandbox/data"),
    262             cache: PathBuf::from("/sandbox/cache"),
    263             logs: PathBuf::from("/sandbox/logs"),
    264             run: PathBuf::from("/sandbox/run"),
    265             secrets: PathBuf::from("/sandbox/secrets"),
    266         };
    267 
    268         let roots = resolver
    269             .resolve(
    270                 RadrootsPathProfile::MobileNative,
    271                 &RadrootsPathOverrides::mobile(mobile_roots.clone()),
    272             )
    273             .expect("resolve mobile_native roots");
    274 
    275         assert_eq!(roots, mobile_roots);
    276     }
    277 
    278     #[test]
    279     fn namespace_derivation_keeps_runtime_segments_explicit() {
    280         let namespace = RadrootsRuntimeNamespace::service("myc").expect("namespace");
    281         let roots = RadrootsPaths::from_base_root("/home/treesap/.radroots");
    282         let namespaced = roots.namespaced(&namespace);
    283 
    284         assert_eq!(
    285             namespaced.config,
    286             PathBuf::from("/home/treesap/.radroots/config/services/myc")
    287         );
    288         assert_eq!(
    289             namespaced.data,
    290             PathBuf::from("/home/treesap/.radroots/data/services/myc")
    291         );
    292         assert_eq!(
    293             namespaced.secrets,
    294             PathBuf::from("/home/treesap/.radroots/secrets/services/myc")
    295         );
    296     }
    297 
    298     #[test]
    299     fn namespace_validation_rejects_path_escape_values() {
    300         let err = RadrootsRuntimeNamespace::app("../cli").expect_err("invalid namespace");
    301         assert_eq!(
    302             err,
    303             RadrootsRuntimePathsError::InvalidNamespaceComponent {
    304                 value: "../cli".to_owned(),
    305             }
    306         );
    307     }
    308 
    309     #[test]
    310     fn interactive_user_unix_requires_home_dir() {
    311         let resolver =
    312             RadrootsPathResolver::new(RadrootsPlatform::Linux, RadrootsHostEnvironment::default());
    313 
    314         let err = resolver
    315             .resolve(
    316                 RadrootsPathProfile::InteractiveUser,
    317                 &RadrootsPathOverrides::default(),
    318             )
    319             .expect_err("interactive_user on linux should require a home dir");
    320 
    321         assert_eq!(
    322             err,
    323             RadrootsRuntimePathsError::MissingHomeDir {
    324                 platform: RadrootsPlatform::Linux,
    325             }
    326         );
    327     }
    328 
    329     #[test]
    330     fn interactive_user_windows_requires_native_dirs() {
    331         let resolver = RadrootsPathResolver::new(
    332             RadrootsPlatform::Windows,
    333             RadrootsHostEnvironment::default(),
    334         );
    335 
    336         let err = resolver
    337             .resolve(
    338                 RadrootsPathProfile::InteractiveUser,
    339                 &RadrootsPathOverrides::default(),
    340             )
    341             .expect_err("interactive_user on windows should require native dirs");
    342 
    343         assert_eq!(err, RadrootsRuntimePathsError::MissingWindowsUserDirs);
    344     }
    345 }