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 }