rhi

Coordinated trade for connected markets
git clone https://radroots.dev/git/rhi.git
Log | Files | Refs | README | LICENSE

runtime_context.rs (6087B)


      1 //! Sealed bootstrap binding for one canonical RHI service instance.
      2 
      3 use core::fmt;
      4 use std::{error::Error, path::Path};
      5 
      6 use radroots_runtime_paths::{
      7     RadrootsPathProfile, RadrootsPathResolver, RadrootsServiceInstanceArtifacts, RuntimeContext,
      8     RuntimeContextBootstrap, RuntimeContextSource, ServiceCredentialArtifactName, ServiceId,
      9     default_service_instance_artifacts, service_credential_artifact_path,
     10 };
     11 
     12 use crate::{RhiBootstrapProfileV1, RhiCliInvocationV1};
     13 
     14 const RHI_SERVICE_ID: &str = "rhi";
     15 const RHI_IDENTITY_ARTIFACT_NAME: &str = "service.identity.ncrypt";
     16 
     17 /// Stable source-free classification for RHI runtime-context failures.
     18 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
     19 pub enum RhiRuntimeContextErrorKind {
     20     InvalidServiceIdentity,
     21     InvalidBootstrapBinding,
     22     PathSelection,
     23 }
     24 
     25 impl RhiRuntimeContextErrorKind {
     26     const fn message(self) -> &'static str {
     27         match self {
     28             Self::InvalidServiceIdentity => "RHI service identity is invalid",
     29             Self::InvalidBootstrapBinding => "RHI bootstrap selectors are inconsistent",
     30             Self::PathSelection => "RHI runtime path selection failed",
     31         }
     32     }
     33 }
     34 
     35 /// One redacted RHI runtime-context failure.
     36 #[derive(Clone, Copy, PartialEq, Eq)]
     37 pub struct RhiRuntimeContextError {
     38     kind: RhiRuntimeContextErrorKind,
     39 }
     40 
     41 impl RhiRuntimeContextError {
     42     const fn new(kind: RhiRuntimeContextErrorKind) -> Self {
     43         Self { kind }
     44     }
     45 
     46     /// Returns the stable failure classification.
     47     #[must_use]
     48     pub const fn kind(self) -> RhiRuntimeContextErrorKind {
     49         self.kind
     50     }
     51 }
     52 
     53 impl fmt::Debug for RhiRuntimeContextError {
     54     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
     55         formatter
     56             .debug_struct("RhiRuntimeContextError")
     57             .field("kind", &self.kind)
     58             .finish()
     59     }
     60 }
     61 
     62 impl fmt::Display for RhiRuntimeContextError {
     63     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
     64         formatter.write_str(self.kind.message())
     65     }
     66 }
     67 
     68 impl Error for RhiRuntimeContextError {}
     69 
     70 /// Immutable canonical paths and bootstrap selection for one RHI instance.
     71 ///
     72 /// Construction is sealed to the validated CLI invocation and the shared
     73 /// runtime-path resolver. Callers cannot forge another service identity, path
     74 /// set, artifact name, or selected configuration path:
     75 ///
     76 /// ```compile_fail
     77 /// use rhi::RhiRuntimeContext;
     78 ///
     79 /// let _ = RhiRuntimeContext {
     80 ///     context: todo!(),
     81 ///     artifacts: todo!(),
     82 ///     selected_config_path: todo!(),
     83 ///     profile: todo!(),
     84 /// };
     85 /// ```
     86 #[derive(Clone, PartialEq, Eq)]
     87 pub struct RhiRuntimeContext {
     88     context: RuntimeContext,
     89     artifacts: RadrootsServiceInstanceArtifacts,
     90     identity_path: std::path::PathBuf,
     91     selected_config_path: std::path::PathBuf,
     92     profile: RhiBootstrapProfileV1,
     93 }
     94 
     95 impl RhiRuntimeContext {
     96     /// Returns the shared immutable service-instance context.
     97     #[must_use]
     98     pub const fn context(&self) -> &RuntimeContext {
     99         &self.context
    100     }
    101 
    102     /// Returns the exact common service artifacts.
    103     #[must_use]
    104     pub const fn artifacts(&self) -> &RadrootsServiceInstanceArtifacts {
    105         &self.artifacts
    106     }
    107 
    108     /// Returns the exact validated encrypted service-identity artifact path.
    109     #[must_use]
    110     pub fn identity_path(&self) -> &Path {
    111         &self.identity_path
    112     }
    113 
    114     /// Returns the explicit or canonical configuration artifact selected once.
    115     #[must_use]
    116     pub fn selected_config_path(&self) -> &Path {
    117         &self.selected_config_path
    118     }
    119 
    120     /// Returns the validated bootstrap profile.
    121     #[must_use]
    122     pub const fn profile(&self) -> RhiBootstrapProfileV1 {
    123         self.profile
    124     }
    125 }
    126 
    127 impl fmt::Debug for RhiRuntimeContext {
    128     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
    129         formatter
    130             .debug_struct("RhiRuntimeContext")
    131             .field("profile", &self.profile)
    132             .field("service", &RHI_SERVICE_ID)
    133             .field("instance", &"[redacted]")
    134             .field("paths", &"[redacted]")
    135             .finish()
    136     }
    137 }
    138 
    139 /// Resolves one validated CLI selection into the sole RHI path authority.
    140 pub fn resolve_rhi_runtime_context(
    141     resolver: &RadrootsPathResolver,
    142     invocation: &RhiCliInvocationV1,
    143 ) -> Result<RhiRuntimeContext, RhiRuntimeContextError> {
    144     let profile = invocation.profile();
    145     let path_profile = match profile {
    146         RhiBootstrapProfileV1::ServiceHost => RadrootsPathProfile::ServiceHost,
    147         RhiBootstrapProfileV1::Interactive => RadrootsPathProfile::InteractiveUser,
    148         RhiBootstrapProfileV1::RepoLocal => RadrootsPathProfile::RepoLocal,
    149     };
    150     let bootstrap = RuntimeContextBootstrap::new(
    151         path_profile,
    152         invocation.repo_local_root().map(Path::to_path_buf),
    153         RuntimeContextSource::BootstrapCli,
    154         RuntimeContextSource::BootstrapCli,
    155     )
    156     .map_err(|_| {
    157         RhiRuntimeContextError::new(RhiRuntimeContextErrorKind::InvalidBootstrapBinding)
    158     })?;
    159     let service = ServiceId::new(RHI_SERVICE_ID).map_err(|_| {
    160         RhiRuntimeContextError::new(RhiRuntimeContextErrorKind::InvalidServiceIdentity)
    161     })?;
    162     let context =
    163         RuntimeContext::resolve(resolver, bootstrap, service, invocation.instance().clone())
    164             .map_err(|_| RhiRuntimeContextError::new(RhiRuntimeContextErrorKind::PathSelection))?;
    165     let artifacts = default_service_instance_artifacts(context.paths());
    166     let identity_name =
    167         ServiceCredentialArtifactName::new(RHI_IDENTITY_ARTIFACT_NAME).map_err(|_| {
    168             RhiRuntimeContextError::new(RhiRuntimeContextErrorKind::InvalidServiceIdentity)
    169         })?;
    170     let identity_path = service_credential_artifact_path(context.paths(), &identity_name);
    171     let selected_config_path = invocation
    172         .config_path()
    173         .map(Path::to_path_buf)
    174         .unwrap_or_else(|| artifacts.config().to_path_buf());
    175 
    176     Ok(RhiRuntimeContext {
    177         context,
    178         artifacts,
    179         identity_path,
    180         selected_config_path,
    181         profile,
    182     })
    183 }