lib

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

commit 2b2505cbba02b5d85ee24d4472e0c9901c6374f0
parent 92b0585b4e1e5dd6dd536e49a4a6efee69de20d7
Author: triesap <tyson@radroots.org>
Date:   Sat, 15 Aug 2026 23:37:15 +0000

service-contracts: validate text before allocation

Diffstat:
Mcontracts/api_baselines/radroots_runtime_paths.txt | 311+------------------------------------------------------------------------------
Mcontracts/api_baselines/radroots_service_host.txt | 30+++++++++++++++---------------
Mcrates/runtime_paths/README | 3+++
Mcrates/runtime_paths/src/conventions.rs | 12+++++++++---
Mcrates/runtime_paths/src/identifier.rs | 58+++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mcrates/runtime_paths/tests/package_boundary.rs | 16++++++++++++++++
Mcrates/service_host/README.md | 5+++++
Mcrates/service_host/src/admin/model.rs | 178++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mcrates/service_host/src/admin/server.rs | 39+++++++++++++++++++++++++++++++--------
Mcrates/service_host/src/config/document.rs | 15+++++++++++----
Mcrates/service_host/src/config/value.rs | 54++++++++++++++++++++++++++++++++++++++++++------------
Mcrates/service_host/src/lifecycle/task.rs | 12++++++++----
Mcrates/service_host/src/operations/metrics.rs | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mcrates/service_host/src/status/reason.rs | 45++++++++++++++++++++++++++++++++++++++++++---
Mcrates/service_host/src/status/service.rs | 24+++++++++++++++++-------
Mcrates/service_host/tests/package_boundary.rs | 22++++++++++++++++++++++
16 files changed, 498 insertions(+), 423 deletions(-)

diff --git a/contracts/api_baselines/radroots_runtime_paths.txt b/contracts/api_baselines/radroots_runtime_paths.txt @@ -4,24 +4,8 @@ pub radroots_runtime_paths::RadrootsPathProfile::InteractiveUser pub radroots_runtime_paths::RadrootsPathProfile::MobileNative pub radroots_runtime_paths::RadrootsPathProfile::RepoLocal pub radroots_runtime_paths::RadrootsPathProfile::ServiceHost -impl core::clone::Clone for radroots_runtime_paths::RadrootsPathProfile -pub fn radroots_runtime_paths::RadrootsPathProfile::clone(&self) -> radroots_runtime_paths::RadrootsPathProfile -impl core::cmp::Eq for radroots_runtime_paths::RadrootsPathProfile -impl core::cmp::PartialEq for radroots_runtime_paths::RadrootsPathProfile -pub fn radroots_runtime_paths::RadrootsPathProfile::eq(&self, &radroots_runtime_paths::RadrootsPathProfile) -> bool -impl core::fmt::Debug for radroots_runtime_paths::RadrootsPathProfile -pub fn radroots_runtime_paths::RadrootsPathProfile::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::fmt::Display for radroots_runtime_paths::RadrootsPathProfile pub fn radroots_runtime_paths::RadrootsPathProfile::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for radroots_runtime_paths::RadrootsPathProfile -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RadrootsPathProfile -impl core::marker::Freeze for radroots_runtime_paths::RadrootsPathProfile -impl core::marker::Send for radroots_runtime_paths::RadrootsPathProfile -impl core::marker::Sync for radroots_runtime_paths::RadrootsPathProfile -impl core::marker::Unpin for radroots_runtime_paths::RadrootsPathProfile -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RadrootsPathProfile -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RadrootsPathProfile -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RadrootsPathProfile pub enum radroots_runtime_paths::RadrootsPlatform pub radroots_runtime_paths::RadrootsPlatform::Android pub radroots_runtime_paths::RadrootsPlatform::Ios @@ -32,24 +16,8 @@ pub radroots_runtime_paths::RadrootsPlatform::Windows impl radroots_runtime_paths::RadrootsPlatform pub fn radroots_runtime_paths::RadrootsPlatform::current() -> Self pub fn radroots_runtime_paths::RadrootsPlatform::is_unix_like(self) -> bool -impl core::clone::Clone for radroots_runtime_paths::RadrootsPlatform -pub fn radroots_runtime_paths::RadrootsPlatform::clone(&self) -> radroots_runtime_paths::RadrootsPlatform -impl core::cmp::Eq for radroots_runtime_paths::RadrootsPlatform -impl core::cmp::PartialEq for radroots_runtime_paths::RadrootsPlatform -pub fn radroots_runtime_paths::RadrootsPlatform::eq(&self, &radroots_runtime_paths::RadrootsPlatform) -> bool -impl core::fmt::Debug for radroots_runtime_paths::RadrootsPlatform -pub fn radroots_runtime_paths::RadrootsPlatform::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::fmt::Display for radroots_runtime_paths::RadrootsPlatform pub fn radroots_runtime_paths::RadrootsPlatform::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for radroots_runtime_paths::RadrootsPlatform -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RadrootsPlatform -impl core::marker::Freeze for radroots_runtime_paths::RadrootsPlatform -impl core::marker::Send for radroots_runtime_paths::RadrootsPlatform -impl core::marker::Sync for radroots_runtime_paths::RadrootsPlatform -impl core::marker::Unpin for radroots_runtime_paths::RadrootsPlatform -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RadrootsPlatform -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RadrootsPlatform -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RadrootsPlatform pub enum radroots_runtime_paths::RadrootsRuntimePathsError pub radroots_runtime_paths::RadrootsRuntimePathsError::InvalidHomeDir pub radroots_runtime_paths::RadrootsRuntimePathsError::InvalidHomeDir::platform: radroots_runtime_paths::RadrootsPlatform @@ -64,94 +32,20 @@ pub radroots_runtime_paths::RadrootsRuntimePathsError::SharedAccountsDataRootMis pub radroots_runtime_paths::RadrootsRuntimePathsError::UnsupportedProfilePlatform pub radroots_runtime_paths::RadrootsRuntimePathsError::UnsupportedProfilePlatform::platform: radroots_runtime_paths::RadrootsPlatform pub radroots_runtime_paths::RadrootsRuntimePathsError::UnsupportedProfilePlatform::profile: radroots_runtime_paths::RadrootsPathProfile -impl core::clone::Clone for radroots_runtime_paths::RadrootsRuntimePathsError -pub fn radroots_runtime_paths::RadrootsRuntimePathsError::clone(&self) -> radroots_runtime_paths::RadrootsRuntimePathsError -impl core::cmp::Eq for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::cmp::PartialEq for radroots_runtime_paths::RadrootsRuntimePathsError -pub fn radroots_runtime_paths::RadrootsRuntimePathsError::eq(&self, &radroots_runtime_paths::RadrootsRuntimePathsError) -> bool -impl core::error::Error for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::fmt::Debug for radroots_runtime_paths::RadrootsRuntimePathsError -pub fn radroots_runtime_paths::RadrootsRuntimePathsError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::fmt::Display for radroots_runtime_paths::RadrootsRuntimePathsError -pub fn radroots_runtime_paths::RadrootsRuntimePathsError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::marker::Freeze for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::marker::Send for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::marker::Sync for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::marker::Unpin for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RadrootsRuntimePathsError -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RadrootsRuntimePathsError pub enum radroots_runtime_paths::RuntimeContextError pub radroots_runtime_paths::RuntimeContextError::InvalidBootstrapBinding pub radroots_runtime_paths::RuntimeContextError::PathSelection -impl core::clone::Clone for radroots_runtime_paths::RuntimeContextError -pub fn radroots_runtime_paths::RuntimeContextError::clone(&self) -> radroots_runtime_paths::RuntimeContextError -impl core::cmp::Eq for radroots_runtime_paths::RuntimeContextError -impl core::cmp::PartialEq for radroots_runtime_paths::RuntimeContextError -pub fn radroots_runtime_paths::RuntimeContextError::eq(&self, &radroots_runtime_paths::RuntimeContextError) -> bool -impl core::error::Error for radroots_runtime_paths::RuntimeContextError -impl core::fmt::Debug for radroots_runtime_paths::RuntimeContextError -pub fn radroots_runtime_paths::RuntimeContextError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::fmt::Display for radroots_runtime_paths::RuntimeContextError -pub fn radroots_runtime_paths::RuntimeContextError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for radroots_runtime_paths::RuntimeContextError -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RuntimeContextError -impl core::marker::Freeze for radroots_runtime_paths::RuntimeContextError -impl core::marker::Send for radroots_runtime_paths::RuntimeContextError -impl core::marker::Sync for radroots_runtime_paths::RuntimeContextError -impl core::marker::Unpin for radroots_runtime_paths::RuntimeContextError -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RuntimeContextError -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RuntimeContextError -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RuntimeContextError pub enum radroots_runtime_paths::RuntimeContextSource pub radroots_runtime_paths::RuntimeContextSource::BootstrapCli pub radroots_runtime_paths::RuntimeContextSource::DerivedPath pub radroots_runtime_paths::RuntimeContextSource::SafeDefault pub radroots_runtime_paths::RuntimeContextSource::Toml -impl core::clone::Clone for radroots_runtime_paths::RuntimeContextSource -pub fn radroots_runtime_paths::RuntimeContextSource::clone(&self) -> radroots_runtime_paths::RuntimeContextSource -impl core::cmp::Eq for radroots_runtime_paths::RuntimeContextSource -impl core::cmp::PartialEq for radroots_runtime_paths::RuntimeContextSource -pub fn radroots_runtime_paths::RuntimeContextSource::eq(&self, &radroots_runtime_paths::RuntimeContextSource) -> bool -impl core::fmt::Debug for radroots_runtime_paths::RuntimeContextSource -pub fn radroots_runtime_paths::RuntimeContextSource::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for radroots_runtime_paths::RuntimeContextSource -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RuntimeContextSource -impl serde_core::ser::Serialize for radroots_runtime_paths::RuntimeContextSource -pub fn radroots_runtime_paths::RuntimeContextSource::serialize<__S>(&self, __S) -> core::result::Result<<__S as serde_core::ser::Serializer>::Ok, <__S as serde_core::ser::Serializer>::Error> where __S: serde_core::ser::Serializer -impl core::marker::Freeze for radroots_runtime_paths::RuntimeContextSource -impl core::marker::Send for radroots_runtime_paths::RuntimeContextSource -impl core::marker::Sync for radroots_runtime_paths::RuntimeContextSource -impl core::marker::Unpin for radroots_runtime_paths::RuntimeContextSource -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RuntimeContextSource -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RuntimeContextSource -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RuntimeContextSource pub enum radroots_runtime_paths::ServiceCredentialArtifactNameError pub radroots_runtime_paths::ServiceCredentialArtifactNameError::Empty pub radroots_runtime_paths::ServiceCredentialArtifactNameError::InvalidBoundary pub radroots_runtime_paths::ServiceCredentialArtifactNameError::InvalidCharacter pub radroots_runtime_paths::ServiceCredentialArtifactNameError::TooLong pub radroots_runtime_paths::ServiceCredentialArtifactNameError::TooLong::maximum: usize -impl core::clone::Clone for radroots_runtime_paths::ServiceCredentialArtifactNameError -pub fn radroots_runtime_paths::ServiceCredentialArtifactNameError::clone(&self) -> radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::cmp::Eq for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::cmp::PartialEq for radroots_runtime_paths::ServiceCredentialArtifactNameError -pub fn radroots_runtime_paths::ServiceCredentialArtifactNameError::eq(&self, &radroots_runtime_paths::ServiceCredentialArtifactNameError) -> bool -impl core::error::Error for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::fmt::Debug for radroots_runtime_paths::ServiceCredentialArtifactNameError -pub fn radroots_runtime_paths::ServiceCredentialArtifactNameError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::fmt::Display for radroots_runtime_paths::ServiceCredentialArtifactNameError -pub fn radroots_runtime_paths::ServiceCredentialArtifactNameError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::marker::StructuralPartialEq for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::marker::Freeze for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::marker::Send for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::marker::Sync for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::marker::Unpin for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::marker::UnsafeUnpin for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::ServiceCredentialArtifactNameError -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::ServiceCredentialArtifactNameError pub enum radroots_runtime_paths::ServiceIdentityError pub radroots_runtime_paths::ServiceIdentityError::Empty pub radroots_runtime_paths::ServiceIdentityError::Empty::kind: radroots_runtime_paths::ServiceIdentityKind @@ -162,60 +56,16 @@ pub radroots_runtime_paths::ServiceIdentityError::InvalidCharacter::kind: radroo pub radroots_runtime_paths::ServiceIdentityError::TooLong pub radroots_runtime_paths::ServiceIdentityError::TooLong::kind: radroots_runtime_paths::ServiceIdentityKind pub radroots_runtime_paths::ServiceIdentityError::TooLong::maximum: usize -impl core::clone::Clone for radroots_runtime_paths::ServiceIdentityError -pub fn radroots_runtime_paths::ServiceIdentityError::clone(&self) -> radroots_runtime_paths::ServiceIdentityError -impl core::cmp::Eq for radroots_runtime_paths::ServiceIdentityError -impl core::cmp::PartialEq for radroots_runtime_paths::ServiceIdentityError -pub fn radroots_runtime_paths::ServiceIdentityError::eq(&self, &radroots_runtime_paths::ServiceIdentityError) -> bool -impl core::error::Error for radroots_runtime_paths::ServiceIdentityError -impl core::fmt::Debug for radroots_runtime_paths::ServiceIdentityError -pub fn radroots_runtime_paths::ServiceIdentityError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::fmt::Display for radroots_runtime_paths::ServiceIdentityError -pub fn radroots_runtime_paths::ServiceIdentityError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for radroots_runtime_paths::ServiceIdentityError -impl core::marker::StructuralPartialEq for radroots_runtime_paths::ServiceIdentityError -impl core::marker::Freeze for radroots_runtime_paths::ServiceIdentityError -impl core::marker::Send for radroots_runtime_paths::ServiceIdentityError -impl core::marker::Sync for radroots_runtime_paths::ServiceIdentityError -impl core::marker::Unpin for radroots_runtime_paths::ServiceIdentityError -impl core::marker::UnsafeUnpin for radroots_runtime_paths::ServiceIdentityError -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::ServiceIdentityError -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::ServiceIdentityError pub enum radroots_runtime_paths::ServiceIdentityKind pub radroots_runtime_paths::ServiceIdentityKind::Instance pub radroots_runtime_paths::ServiceIdentityKind::Service -impl core::clone::Clone for radroots_runtime_paths::ServiceIdentityKind -pub fn radroots_runtime_paths::ServiceIdentityKind::clone(&self) -> radroots_runtime_paths::ServiceIdentityKind -impl core::cmp::Eq for radroots_runtime_paths::ServiceIdentityKind -impl core::cmp::PartialEq for radroots_runtime_paths::ServiceIdentityKind -pub fn radroots_runtime_paths::ServiceIdentityKind::eq(&self, &radroots_runtime_paths::ServiceIdentityKind) -> bool -impl core::fmt::Debug for radroots_runtime_paths::ServiceIdentityKind -pub fn radroots_runtime_paths::ServiceIdentityKind::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::fmt::Display for radroots_runtime_paths::ServiceIdentityKind pub fn radroots_runtime_paths::ServiceIdentityKind::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::Copy for radroots_runtime_paths::ServiceIdentityKind -impl core::marker::StructuralPartialEq for radroots_runtime_paths::ServiceIdentityKind -impl core::marker::Freeze for radroots_runtime_paths::ServiceIdentityKind -impl core::marker::Send for radroots_runtime_paths::ServiceIdentityKind -impl core::marker::Sync for radroots_runtime_paths::ServiceIdentityKind -impl core::marker::Unpin for radroots_runtime_paths::ServiceIdentityKind -impl core::marker::UnsafeUnpin for radroots_runtime_paths::ServiceIdentityKind -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::ServiceIdentityKind -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::ServiceIdentityKind pub struct radroots_runtime_paths::InstanceId(_) impl radroots_runtime_paths::InstanceId pub fn radroots_runtime_paths::InstanceId::as_str(&self) -> &str pub fn radroots_runtime_paths::InstanceId::into_string(self) -> alloc::string::String -pub fn radroots_runtime_paths::InstanceId::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_runtime_paths::ServiceIdentityError> -impl core::clone::Clone for radroots_runtime_paths::InstanceId -pub fn radroots_runtime_paths::InstanceId::clone(&self) -> radroots_runtime_paths::InstanceId -impl core::cmp::Eq for radroots_runtime_paths::InstanceId -impl core::cmp::Ord for radroots_runtime_paths::InstanceId -pub fn radroots_runtime_paths::InstanceId::cmp(&self, &radroots_runtime_paths::InstanceId) -> core::cmp::Ordering -impl core::cmp::PartialEq for radroots_runtime_paths::InstanceId -pub fn radroots_runtime_paths::InstanceId::eq(&self, &radroots_runtime_paths::InstanceId) -> bool -impl core::cmp::PartialOrd for radroots_runtime_paths::InstanceId -pub fn radroots_runtime_paths::InstanceId::partial_cmp(&self, &radroots_runtime_paths::InstanceId) -> core::option::Option<core::cmp::Ordering> +pub fn radroots_runtime_paths::InstanceId::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_runtime_paths::ServiceIdentityError> impl core::convert::AsRef<str> for radroots_runtime_paths::InstanceId pub fn radroots_runtime_paths::InstanceId::as_ref(&self) -> &str impl core::convert::From<radroots_runtime_paths::InstanceId> for alloc::string::String @@ -223,13 +73,8 @@ pub fn alloc::string::String::from(radroots_runtime_paths::InstanceId) -> Self impl core::convert::TryFrom<alloc::string::String> for radroots_runtime_paths::InstanceId pub type radroots_runtime_paths::InstanceId::Error = radroots_runtime_paths::ServiceIdentityError pub fn radroots_runtime_paths::InstanceId::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error> -impl core::fmt::Debug for radroots_runtime_paths::InstanceId -pub fn radroots_runtime_paths::InstanceId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::fmt::Display for radroots_runtime_paths::InstanceId pub fn radroots_runtime_paths::InstanceId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::hash::Hash for radroots_runtime_paths::InstanceId -pub fn radroots_runtime_paths::InstanceId::hash<__H: core::hash::Hasher>(&self, &mut __H) -impl core::marker::StructuralPartialEq for radroots_runtime_paths::InstanceId impl core::str::traits::FromStr for radroots_runtime_paths::InstanceId pub type radroots_runtime_paths::InstanceId::Err = radroots_runtime_paths::ServiceIdentityError pub fn radroots_runtime_paths::InstanceId::from_str(&str) -> core::result::Result<Self, Self::Err> @@ -237,13 +82,6 @@ impl serde_core::ser::Serialize for radroots_runtime_paths::InstanceId pub fn radroots_runtime_paths::InstanceId::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer impl<'de> serde_core::de::Deserialize<'de> for radroots_runtime_paths::InstanceId pub fn radroots_runtime_paths::InstanceId::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de> -impl core::marker::Freeze for radroots_runtime_paths::InstanceId -impl core::marker::Send for radroots_runtime_paths::InstanceId -impl core::marker::Sync for radroots_runtime_paths::InstanceId -impl core::marker::Unpin for radroots_runtime_paths::InstanceId -impl core::marker::UnsafeUnpin for radroots_runtime_paths::InstanceId -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::InstanceId -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::InstanceId pub struct radroots_runtime_paths::RadrootsHostEnvironment pub radroots_runtime_paths::RadrootsHostEnvironment::appdata_dir: core::option::Option<std::path::PathBuf> pub radroots_runtime_paths::RadrootsHostEnvironment::home_dir: core::option::Option<std::path::PathBuf> @@ -253,63 +91,18 @@ pub radroots_runtime_paths::RadrootsHostEnvironment::xdg_config_home: core::opti pub radroots_runtime_paths::RadrootsHostEnvironment::xdg_data_home: core::option::Option<std::path::PathBuf> pub radroots_runtime_paths::RadrootsHostEnvironment::xdg_runtime_dir: core::option::Option<std::path::PathBuf> pub radroots_runtime_paths::RadrootsHostEnvironment::xdg_state_home: core::option::Option<std::path::PathBuf> -impl core::clone::Clone for radroots_runtime_paths::RadrootsHostEnvironment -pub fn radroots_runtime_paths::RadrootsHostEnvironment::clone(&self) -> radroots_runtime_paths::RadrootsHostEnvironment -impl core::cmp::Eq for radroots_runtime_paths::RadrootsHostEnvironment -impl core::cmp::PartialEq for radroots_runtime_paths::RadrootsHostEnvironment -pub fn radroots_runtime_paths::RadrootsHostEnvironment::eq(&self, &radroots_runtime_paths::RadrootsHostEnvironment) -> bool -impl core::default::Default for radroots_runtime_paths::RadrootsHostEnvironment -pub fn radroots_runtime_paths::RadrootsHostEnvironment::default() -> radroots_runtime_paths::RadrootsHostEnvironment -impl core::fmt::Debug for radroots_runtime_paths::RadrootsHostEnvironment -pub fn radroots_runtime_paths::RadrootsHostEnvironment::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RadrootsHostEnvironment -impl core::marker::Freeze for radroots_runtime_paths::RadrootsHostEnvironment -impl core::marker::Send for radroots_runtime_paths::RadrootsHostEnvironment -impl core::marker::Sync for radroots_runtime_paths::RadrootsHostEnvironment -impl core::marker::Unpin for radroots_runtime_paths::RadrootsHostEnvironment -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RadrootsHostEnvironment -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RadrootsHostEnvironment -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RadrootsHostEnvironment pub struct radroots_runtime_paths::RadrootsPathResolver impl radroots_runtime_paths::RadrootsPathResolver pub fn radroots_runtime_paths::RadrootsPathResolver::new(radroots_runtime_paths::RadrootsPlatform, radroots_runtime_paths::RadrootsHostEnvironment) -> Self pub fn radroots_runtime_paths::RadrootsPathResolver::platform(&self) -> radroots_runtime_paths::RadrootsPlatform -impl core::clone::Clone for radroots_runtime_paths::RadrootsPathResolver -pub fn radroots_runtime_paths::RadrootsPathResolver::clone(&self) -> radroots_runtime_paths::RadrootsPathResolver -impl core::cmp::Eq for radroots_runtime_paths::RadrootsPathResolver -impl core::cmp::PartialEq for radroots_runtime_paths::RadrootsPathResolver -pub fn radroots_runtime_paths::RadrootsPathResolver::eq(&self, &radroots_runtime_paths::RadrootsPathResolver) -> bool -impl core::fmt::Debug for radroots_runtime_paths::RadrootsPathResolver -pub fn radroots_runtime_paths::RadrootsPathResolver::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RadrootsPathResolver -impl core::marker::Freeze for radroots_runtime_paths::RadrootsPathResolver -impl core::marker::Send for radroots_runtime_paths::RadrootsPathResolver -impl core::marker::Sync for radroots_runtime_paths::RadrootsPathResolver -impl core::marker::Unpin for radroots_runtime_paths::RadrootsPathResolver -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RadrootsPathResolver -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RadrootsPathResolver -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RadrootsPathResolver pub struct radroots_runtime_paths::RadrootsServiceInstanceArtifacts impl radroots_runtime_paths::RadrootsServiceInstanceArtifacts pub fn radroots_runtime_paths::RadrootsServiceInstanceArtifacts::admin_socket(&self) -> &std::path::Path pub fn radroots_runtime_paths::RadrootsServiceInstanceArtifacts::config(&self) -> &std::path::Path pub fn radroots_runtime_paths::RadrootsServiceInstanceArtifacts::state_database(&self) -> &std::path::Path pub fn radroots_runtime_paths::RadrootsServiceInstanceArtifacts::state_lock(&self) -> &std::path::Path -impl core::clone::Clone for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -pub fn radroots_runtime_paths::RadrootsServiceInstanceArtifacts::clone(&self) -> radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::cmp::Eq for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::cmp::PartialEq for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -pub fn radroots_runtime_paths::RadrootsServiceInstanceArtifacts::eq(&self, &radroots_runtime_paths::RadrootsServiceInstanceArtifacts) -> bool impl core::fmt::Debug for radroots_runtime_paths::RadrootsServiceInstanceArtifacts pub fn radroots_runtime_paths::RadrootsServiceInstanceArtifacts::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::marker::Freeze for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::marker::Send for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::marker::Sync for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::marker::Unpin for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RadrootsServiceInstanceArtifacts -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RadrootsServiceInstanceArtifacts pub struct radroots_runtime_paths::RadrootsServiceInstancePaths impl radroots_runtime_paths::RadrootsServiceInstancePaths pub fn radroots_runtime_paths::RadrootsServiceInstancePaths::cache(&self) -> &std::path::Path @@ -318,21 +111,6 @@ pub fn radroots_runtime_paths::RadrootsServiceInstancePaths::logs(&self) -> &std pub fn radroots_runtime_paths::RadrootsServiceInstancePaths::run(&self) -> &std::path::Path pub fn radroots_runtime_paths::RadrootsServiceInstancePaths::secrets(&self) -> &std::path::Path pub fn radroots_runtime_paths::RadrootsServiceInstancePaths::state(&self) -> &std::path::Path -impl core::clone::Clone for radroots_runtime_paths::RadrootsServiceInstancePaths -pub fn radroots_runtime_paths::RadrootsServiceInstancePaths::clone(&self) -> radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::cmp::Eq for radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::cmp::PartialEq for radroots_runtime_paths::RadrootsServiceInstancePaths -pub fn radroots_runtime_paths::RadrootsServiceInstancePaths::eq(&self, &radroots_runtime_paths::RadrootsServiceInstancePaths) -> bool -impl core::fmt::Debug for radroots_runtime_paths::RadrootsServiceInstancePaths -pub fn radroots_runtime_paths::RadrootsServiceInstancePaths::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::marker::Freeze for radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::marker::Send for radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::marker::Sync for radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::marker::Unpin for radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RadrootsServiceInstancePaths -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RadrootsServiceInstancePaths pub struct radroots_runtime_paths::RuntimeContext impl radroots_runtime_paths::RuntimeContext pub fn radroots_runtime_paths::RuntimeContext::instance(&self) -> &radroots_runtime_paths::InstanceId @@ -341,42 +119,16 @@ pub fn radroots_runtime_paths::RuntimeContext::profile(&self) -> radroots_runtim pub fn radroots_runtime_paths::RuntimeContext::resolve(&radroots_runtime_paths::RadrootsPathResolver, radroots_runtime_paths::RuntimeContextBootstrap, radroots_runtime_paths::ServiceId, radroots_runtime_paths::InstanceId) -> core::result::Result<Self, radroots_runtime_paths::RuntimeContextError> pub fn radroots_runtime_paths::RuntimeContext::service(&self) -> &radroots_runtime_paths::ServiceId pub fn radroots_runtime_paths::RuntimeContext::sources(&self) -> &radroots_runtime_paths::RuntimeContextSources -impl core::clone::Clone for radroots_runtime_paths::RuntimeContext -pub fn radroots_runtime_paths::RuntimeContext::clone(&self) -> radroots_runtime_paths::RuntimeContext -impl core::cmp::Eq for radroots_runtime_paths::RuntimeContext -impl core::cmp::PartialEq for radroots_runtime_paths::RuntimeContext -pub fn radroots_runtime_paths::RuntimeContext::eq(&self, &radroots_runtime_paths::RuntimeContext) -> bool impl core::fmt::Debug for radroots_runtime_paths::RuntimeContext pub fn radroots_runtime_paths::RuntimeContext::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RuntimeContext impl serde_core::ser::Serialize for radroots_runtime_paths::RuntimeContext pub fn radroots_runtime_paths::RuntimeContext::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer -impl core::marker::Freeze for radroots_runtime_paths::RuntimeContext -impl core::marker::Send for radroots_runtime_paths::RuntimeContext -impl core::marker::Sync for radroots_runtime_paths::RuntimeContext -impl core::marker::Unpin for radroots_runtime_paths::RuntimeContext -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RuntimeContext -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RuntimeContext -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RuntimeContext pub struct radroots_runtime_paths::RuntimeContextBootstrap impl radroots_runtime_paths::RuntimeContextBootstrap pub fn radroots_runtime_paths::RuntimeContextBootstrap::new(radroots_runtime_paths::RadrootsPathProfile, core::option::Option<std::path::PathBuf>, radroots_runtime_paths::RuntimeContextSource, radroots_runtime_paths::RuntimeContextSource) -> core::result::Result<Self, radroots_runtime_paths::RuntimeContextError> pub fn radroots_runtime_paths::RuntimeContextBootstrap::profile(&self) -> radroots_runtime_paths::RadrootsPathProfile -impl core::clone::Clone for radroots_runtime_paths::RuntimeContextBootstrap -pub fn radroots_runtime_paths::RuntimeContextBootstrap::clone(&self) -> radroots_runtime_paths::RuntimeContextBootstrap -impl core::cmp::Eq for radroots_runtime_paths::RuntimeContextBootstrap -impl core::cmp::PartialEq for radroots_runtime_paths::RuntimeContextBootstrap -pub fn radroots_runtime_paths::RuntimeContextBootstrap::eq(&self, &radroots_runtime_paths::RuntimeContextBootstrap) -> bool impl core::fmt::Debug for radroots_runtime_paths::RuntimeContextBootstrap pub fn radroots_runtime_paths::RuntimeContextBootstrap::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RuntimeContextBootstrap -impl core::marker::Freeze for radroots_runtime_paths::RuntimeContextBootstrap -impl core::marker::Send for radroots_runtime_paths::RuntimeContextBootstrap -impl core::marker::Sync for radroots_runtime_paths::RuntimeContextBootstrap -impl core::marker::Unpin for radroots_runtime_paths::RuntimeContextBootstrap -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RuntimeContextBootstrap -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RuntimeContextBootstrap -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RuntimeContextBootstrap pub struct radroots_runtime_paths::RuntimeContextSources impl radroots_runtime_paths::RuntimeContextSources pub fn radroots_runtime_paths::RuntimeContextSources::instance(&self) -> radroots_runtime_paths::RuntimeContextSource @@ -384,64 +136,19 @@ pub fn radroots_runtime_paths::RuntimeContextSources::paths(&self) -> radroots_r pub fn radroots_runtime_paths::RuntimeContextSources::profile(&self) -> radroots_runtime_paths::RuntimeContextSource pub fn radroots_runtime_paths::RuntimeContextSources::repo_local_root(&self) -> core::option::Option<radroots_runtime_paths::RuntimeContextSource> pub fn radroots_runtime_paths::RuntimeContextSources::service(&self) -> radroots_runtime_paths::RuntimeContextSource -impl core::clone::Clone for radroots_runtime_paths::RuntimeContextSources -pub fn radroots_runtime_paths::RuntimeContextSources::clone(&self) -> radroots_runtime_paths::RuntimeContextSources -impl core::cmp::Eq for radroots_runtime_paths::RuntimeContextSources -impl core::cmp::PartialEq for radroots_runtime_paths::RuntimeContextSources -pub fn radroots_runtime_paths::RuntimeContextSources::eq(&self, &radroots_runtime_paths::RuntimeContextSources) -> bool -impl core::fmt::Debug for radroots_runtime_paths::RuntimeContextSources -pub fn radroots_runtime_paths::RuntimeContextSources::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::marker::StructuralPartialEq for radroots_runtime_paths::RuntimeContextSources -impl serde_core::ser::Serialize for radroots_runtime_paths::RuntimeContextSources -pub fn radroots_runtime_paths::RuntimeContextSources::serialize<__S>(&self, __S) -> core::result::Result<<__S as serde_core::ser::Serializer>::Ok, <__S as serde_core::ser::Serializer>::Error> where __S: serde_core::ser::Serializer -impl core::marker::Freeze for radroots_runtime_paths::RuntimeContextSources -impl core::marker::Send for radroots_runtime_paths::RuntimeContextSources -impl core::marker::Sync for radroots_runtime_paths::RuntimeContextSources -impl core::marker::Unpin for radroots_runtime_paths::RuntimeContextSources -impl core::marker::UnsafeUnpin for radroots_runtime_paths::RuntimeContextSources -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::RuntimeContextSources -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::RuntimeContextSources pub struct radroots_runtime_paths::ServiceCredentialArtifactName(_) impl radroots_runtime_paths::ServiceCredentialArtifactName pub fn radroots_runtime_paths::ServiceCredentialArtifactName::as_str(&self) -> &str -pub fn radroots_runtime_paths::ServiceCredentialArtifactName::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_runtime_paths::ServiceCredentialArtifactNameError> -impl core::clone::Clone for radroots_runtime_paths::ServiceCredentialArtifactName -pub fn radroots_runtime_paths::ServiceCredentialArtifactName::clone(&self) -> radroots_runtime_paths::ServiceCredentialArtifactName -impl core::cmp::Eq for radroots_runtime_paths::ServiceCredentialArtifactName -impl core::cmp::Ord for radroots_runtime_paths::ServiceCredentialArtifactName -pub fn radroots_runtime_paths::ServiceCredentialArtifactName::cmp(&self, &radroots_runtime_paths::ServiceCredentialArtifactName) -> core::cmp::Ordering -impl core::cmp::PartialEq for radroots_runtime_paths::ServiceCredentialArtifactName -pub fn radroots_runtime_paths::ServiceCredentialArtifactName::eq(&self, &radroots_runtime_paths::ServiceCredentialArtifactName) -> bool -impl core::cmp::PartialOrd for radroots_runtime_paths::ServiceCredentialArtifactName -pub fn radroots_runtime_paths::ServiceCredentialArtifactName::partial_cmp(&self, &radroots_runtime_paths::ServiceCredentialArtifactName) -> core::option::Option<core::cmp::Ordering> +pub fn radroots_runtime_paths::ServiceCredentialArtifactName::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_runtime_paths::ServiceCredentialArtifactNameError> impl core::convert::AsRef<str> for radroots_runtime_paths::ServiceCredentialArtifactName pub fn radroots_runtime_paths::ServiceCredentialArtifactName::as_ref(&self) -> &str impl core::fmt::Debug for radroots_runtime_paths::ServiceCredentialArtifactName pub fn radroots_runtime_paths::ServiceCredentialArtifactName::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::hash::Hash for radroots_runtime_paths::ServiceCredentialArtifactName -pub fn radroots_runtime_paths::ServiceCredentialArtifactName::hash<__H: core::hash::Hasher>(&self, &mut __H) -impl core::marker::StructuralPartialEq for radroots_runtime_paths::ServiceCredentialArtifactName -impl core::marker::Freeze for radroots_runtime_paths::ServiceCredentialArtifactName -impl core::marker::Send for radroots_runtime_paths::ServiceCredentialArtifactName -impl core::marker::Sync for radroots_runtime_paths::ServiceCredentialArtifactName -impl core::marker::Unpin for radroots_runtime_paths::ServiceCredentialArtifactName -impl core::marker::UnsafeUnpin for radroots_runtime_paths::ServiceCredentialArtifactName -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::ServiceCredentialArtifactName -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::ServiceCredentialArtifactName pub struct radroots_runtime_paths::ServiceId(_) impl radroots_runtime_paths::ServiceId pub fn radroots_runtime_paths::ServiceId::as_str(&self) -> &str pub fn radroots_runtime_paths::ServiceId::into_string(self) -> alloc::string::String -pub fn radroots_runtime_paths::ServiceId::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_runtime_paths::ServiceIdentityError> -impl core::clone::Clone for radroots_runtime_paths::ServiceId -pub fn radroots_runtime_paths::ServiceId::clone(&self) -> radroots_runtime_paths::ServiceId -impl core::cmp::Eq for radroots_runtime_paths::ServiceId -impl core::cmp::Ord for radroots_runtime_paths::ServiceId -pub fn radroots_runtime_paths::ServiceId::cmp(&self, &radroots_runtime_paths::ServiceId) -> core::cmp::Ordering -impl core::cmp::PartialEq for radroots_runtime_paths::ServiceId -pub fn radroots_runtime_paths::ServiceId::eq(&self, &radroots_runtime_paths::ServiceId) -> bool -impl core::cmp::PartialOrd for radroots_runtime_paths::ServiceId -pub fn radroots_runtime_paths::ServiceId::partial_cmp(&self, &radroots_runtime_paths::ServiceId) -> core::option::Option<core::cmp::Ordering> +pub fn radroots_runtime_paths::ServiceId::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_runtime_paths::ServiceIdentityError> impl core::convert::AsRef<str> for radroots_runtime_paths::ServiceId pub fn radroots_runtime_paths::ServiceId::as_ref(&self) -> &str impl core::convert::From<radroots_runtime_paths::ServiceId> for alloc::string::String @@ -449,13 +156,8 @@ pub fn alloc::string::String::from(radroots_runtime_paths::ServiceId) -> Self impl core::convert::TryFrom<alloc::string::String> for radroots_runtime_paths::ServiceId pub type radroots_runtime_paths::ServiceId::Error = radroots_runtime_paths::ServiceIdentityError pub fn radroots_runtime_paths::ServiceId::try_from(alloc::string::String) -> core::result::Result<Self, Self::Error> -impl core::fmt::Debug for radroots_runtime_paths::ServiceId -pub fn radroots_runtime_paths::ServiceId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl core::fmt::Display for radroots_runtime_paths::ServiceId pub fn radroots_runtime_paths::ServiceId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result -impl core::hash::Hash for radroots_runtime_paths::ServiceId -pub fn radroots_runtime_paths::ServiceId::hash<__H: core::hash::Hasher>(&self, &mut __H) -impl core::marker::StructuralPartialEq for radroots_runtime_paths::ServiceId impl core::str::traits::FromStr for radroots_runtime_paths::ServiceId pub type radroots_runtime_paths::ServiceId::Err = radroots_runtime_paths::ServiceIdentityError pub fn radroots_runtime_paths::ServiceId::from_str(&str) -> core::result::Result<Self, Self::Err> @@ -463,13 +165,6 @@ impl serde_core::ser::Serialize for radroots_runtime_paths::ServiceId pub fn radroots_runtime_paths::ServiceId::serialize<S>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error> where S: serde_core::ser::Serializer impl<'de> serde_core::de::Deserialize<'de> for radroots_runtime_paths::ServiceId pub fn radroots_runtime_paths::ServiceId::deserialize<D>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error> where D: serde_core::de::Deserializer<'de> -impl core::marker::Freeze for radroots_runtime_paths::ServiceId -impl core::marker::Send for radroots_runtime_paths::ServiceId -impl core::marker::Sync for radroots_runtime_paths::ServiceId -impl core::marker::Unpin for radroots_runtime_paths::ServiceId -impl core::marker::UnsafeUnpin for radroots_runtime_paths::ServiceId -impl core::panic::unwind_safe::RefUnwindSafe for radroots_runtime_paths::ServiceId -impl core::panic::unwind_safe::UnwindSafe for radroots_runtime_paths::ServiceId pub const radroots_runtime_paths::DEFAULT_CONFIG_FILE_NAME: &str pub const radroots_runtime_paths::DEFAULT_SHARED_GEONAMES_NAMESPACE: &str pub const radroots_runtime_paths::DEFAULT_SHARED_GEONAMES_NAMESPACE_KIND: &str diff --git a/contracts/api_baselines/radroots_service_host.txt b/contracts/api_baselines/radroots_service_host.txt @@ -643,7 +643,7 @@ pub fn radroots_service_host::AdminContractVersionError::fmt(&self, &mut core::f pub struct radroots_service_host::AdminCorrelationId(_) impl radroots_service_host::AdminCorrelationId pub fn radroots_service_host::AdminCorrelationId::as_str(&self) -> &str -pub fn radroots_service_host::AdminCorrelationId::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::AdminIdentifierError> +pub fn radroots_service_host::AdminCorrelationId::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::AdminIdentifierError> impl core::fmt::Display for radroots_service_host::AdminCorrelationId pub fn radroots_service_host::AdminCorrelationId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl serde_core::ser::Serialize for radroots_service_host::AdminCorrelationId @@ -659,7 +659,7 @@ pub const fn radroots_service_host::AdminError::new(radroots_service_host::Admin pub struct radroots_service_host::AdminErrorCode(_) impl radroots_service_host::AdminErrorCode pub fn radroots_service_host::AdminErrorCode::as_str(&self) -> &str -pub fn radroots_service_host::AdminErrorCode::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::AdminErrorCodeError> +pub fn radroots_service_host::AdminErrorCode::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::AdminErrorCodeError> impl core::fmt::Display for radroots_service_host::AdminErrorCode pub fn radroots_service_host::AdminErrorCode::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl serde_core::ser::Serialize for radroots_service_host::AdminErrorCode @@ -669,7 +669,7 @@ pub fn radroots_service_host::AdminErrorCode::deserialize<D>(D) -> core::result: pub struct radroots_service_host::AdminErrorMessage(_) impl radroots_service_host::AdminErrorMessage pub fn radroots_service_host::AdminErrorMessage::as_str(&self) -> &str -pub fn radroots_service_host::AdminErrorMessage::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::AdminErrorMessageError> +pub fn radroots_service_host::AdminErrorMessage::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::AdminErrorMessageError> impl core::fmt::Display for radroots_service_host::AdminErrorMessage pub fn radroots_service_host::AdminErrorMessage::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl serde_core::ser::Serialize for radroots_service_host::AdminErrorMessage @@ -696,7 +696,7 @@ pub fn radroots_service_host::AdminMutationRequest<T>::fmt(&self, &mut core::fmt pub struct radroots_service_host::AdminOperationId(_) impl radroots_service_host::AdminOperationId pub fn radroots_service_host::AdminOperationId::as_str(&self) -> &str -pub fn radroots_service_host::AdminOperationId::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::AdminIdentifierError> +pub fn radroots_service_host::AdminOperationId::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::AdminIdentifierError> impl core::fmt::Display for radroots_service_host::AdminOperationId pub fn radroots_service_host::AdminOperationId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result impl serde_core::ser::Serialize for radroots_service_host::AdminOperationId @@ -736,11 +736,11 @@ pub fn radroots_service_host::AdminRouteOutcome::fmt(&self, &mut core::fmt::Form pub struct radroots_service_host::AdminRoutePath impl radroots_service_host::AdminRoutePath pub fn radroots_service_host::AdminRoutePath::as_str(&self) -> &str -pub fn radroots_service_host::AdminRoutePath::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::AdminRoutePathError> +pub fn radroots_service_host::AdminRoutePath::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::AdminRoutePathError> pub struct radroots_service_host::AdminRouter impl radroots_service_host::AdminRouter pub fn radroots_service_host::AdminRouter::new() -> Self -pub fn radroots_service_host::AdminRouter::route<F, Fut>(&mut self, radroots_service_host::AdminHttpMethod, impl core::convert::Into<alloc::string::String>, F) -> core::result::Result<(), radroots_service_host::AdminRouteRegistrationError> where F: core::ops::function::Fn(radroots_service_host::AdminRequest) -> Fut + core::marker::Send + core::marker::Sync + 'static, Fut: core::future::future::Future<Output = radroots_service_host::AdminRouteOutcome> + core::marker::Send + 'static +pub fn radroots_service_host::AdminRouter::route<F, Fut>(&mut self, radroots_service_host::AdminHttpMethod, impl core::convert::AsRef<str>, F) -> core::result::Result<(), radroots_service_host::AdminRouteRegistrationError> where F: core::ops::function::Fn(radroots_service_host::AdminRequest) -> Fut + core::marker::Send + core::marker::Sync + 'static, Fut: core::future::future::Future<Output = radroots_service_host::AdminRouteOutcome> + core::marker::Send + 'static impl core::fmt::Debug for radroots_service_host::AdminRouter pub fn radroots_service_host::AdminRouter::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub struct radroots_service_host::AdminServer @@ -876,7 +876,7 @@ impl core::fmt::Display for radroots_service_host::ConfigDocumentError pub fn radroots_service_host::ConfigDocumentError::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub struct radroots_service_host::ConfigDocumentExpectation impl radroots_service_host::ConfigDocumentExpectation -pub fn radroots_service_host::ConfigDocumentExpectation::new(impl core::convert::Into<alloc::boxed::Box<str>>, u32) -> core::result::Result<Self, radroots_service_host::ConfigDocumentExpectationError> +pub fn radroots_service_host::ConfigDocumentExpectation::new(impl core::convert::AsRef<str>, u32) -> core::result::Result<Self, radroots_service_host::ConfigDocumentExpectationError> pub fn radroots_service_host::ConfigDocumentExpectation::schema(&self) -> &str pub const fn radroots_service_host::ConfigDocumentExpectation::schema_version(&self) -> u32 impl core::fmt::Debug for radroots_service_host::ConfigDocumentExpectation @@ -916,14 +916,14 @@ pub fn radroots_service_host::HostError::fmt(&self, &mut core::fmt::Formatter<'_ pub struct radroots_service_host::MetricComponentId(_) impl radroots_service_host::MetricComponentId pub fn radroots_service_host::MetricComponentId::as_str(&self) -> &str -pub fn radroots_service_host::MetricComponentId::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::MetricsContractError> +pub fn radroots_service_host::MetricComponentId::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::MetricsContractError> pub struct radroots_service_host::MetricDescriptor impl radroots_service_host::MetricDescriptor pub const fn radroots_service_host::MetricDescriptor::group(&self) -> radroots_service_host::CommonMetricGroup pub const fn radroots_service_host::MetricDescriptor::kind(&self) -> radroots_service_host::MetricKind pub fn radroots_service_host::MetricDescriptor::label_keys(&self) -> &[radroots_service_host::MetricLabelKey] pub fn radroots_service_host::MetricDescriptor::name(&self) -> &radroots_service_host::MetricName -pub fn radroots_service_host::MetricDescriptor::new(radroots_service_host::CommonMetricGroup, radroots_service_host::MetricName, impl core::convert::Into<alloc::string::String>, radroots_service_host::MetricKind, impl core::iter::traits::collect::IntoIterator<Item = radroots_service_host::MetricLabelKey>) -> core::result::Result<Self, radroots_service_host::MetricsContractError> +pub fn radroots_service_host::MetricDescriptor::new(radroots_service_host::CommonMetricGroup, radroots_service_host::MetricName, impl core::convert::AsRef<str>, radroots_service_host::MetricKind, impl core::iter::traits::collect::IntoIterator<Item = radroots_service_host::MetricLabelKey>) -> core::result::Result<Self, radroots_service_host::MetricsContractError> impl core::fmt::Debug for radroots_service_host::MetricDescriptor pub fn radroots_service_host::MetricDescriptor::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub struct radroots_service_host::MetricLabel @@ -945,7 +945,7 @@ pub fn radroots_service_host::MetricLabel::fmt(&self, &mut core::fmt::Formatter< pub struct radroots_service_host::MetricName(_) impl radroots_service_host::MetricName pub fn radroots_service_host::MetricName::as_str(&self) -> &str -pub fn radroots_service_host::MetricName::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::MetricsContractError> +pub fn radroots_service_host::MetricName::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::MetricsContractError> pub struct radroots_service_host::MetricSample impl radroots_service_host::MetricSample pub fn radroots_service_host::MetricSample::labels(&self) -> &[radroots_service_host::MetricLabel] @@ -1062,7 +1062,7 @@ pub const fn radroots_service_host::Readiness::is_ready(self) -> bool pub struct radroots_service_host::ReasonCode(_) impl radroots_service_host::ReasonCode pub fn radroots_service_host::ReasonCode::as_str(&self) -> &str -pub fn radroots_service_host::ReasonCode::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::StatusContractError> +pub fn radroots_service_host::ReasonCode::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::StatusContractError> impl core::convert::From<radroots_service_host::CommonReasonCode> for radroots_service_host::ReasonCode pub fn radroots_service_host::ReasonCode::from(radroots_service_host::CommonReasonCode) -> Self impl core::fmt::Display for radroots_service_host::ReasonCode @@ -1108,7 +1108,7 @@ pub fn radroots_service_host::ServiceStatus<D>::serialize<S>(&self, S) -> core:: pub struct radroots_service_host::Sha256Digest(_) impl radroots_service_host::Sha256Digest pub fn radroots_service_host::Sha256Digest::as_str(&self) -> &str -pub fn radroots_service_host::Sha256Digest::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::StatusModelError> +pub fn radroots_service_host::Sha256Digest::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::StatusModelError> pub struct radroots_service_host::ShutdownPhaseFailure impl radroots_service_host::ShutdownPhaseFailure pub const fn radroots_service_host::ShutdownPhaseFailure::error(&self) -> &radroots_service_host::HostError @@ -1126,12 +1126,12 @@ pub const fn radroots_service_host::ShutdownSummary::disposition(self) -> radroo pub struct radroots_service_host::StableRelayId(_) impl radroots_service_host::StableRelayId pub fn radroots_service_host::StableRelayId::as_str(&self) -> &str -pub fn radroots_service_host::StableRelayId::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::MetricsContractError> +pub fn radroots_service_host::StableRelayId::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::MetricsContractError> pub struct radroots_service_host::StatusBuildInfo<'a> pub struct radroots_service_host::StatusId(_) impl radroots_service_host::StatusId pub fn radroots_service_host::StatusId::as_str(&self) -> &str -pub fn radroots_service_host::StatusId::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::StatusModelError> +pub fn radroots_service_host::StatusId::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::StatusModelError> pub struct radroots_service_host::StatusPublisherDropped impl core::error::Error for radroots_service_host::StatusPublisherDropped impl core::fmt::Display for radroots_service_host::StatusPublisherDropped @@ -1176,7 +1176,7 @@ pub const fn radroots_service_host::TaskMetadata::shutdown_phase(&self) -> core: pub struct radroots_service_host::TaskName(_) impl radroots_service_host::TaskName pub fn radroots_service_host::TaskName::as_str(&self) -> &str -pub fn radroots_service_host::TaskName::new(impl core::convert::Into<alloc::string::String>) -> core::result::Result<Self, radroots_service_host::TaskMetadataError> +pub fn radroots_service_host::TaskName::new(impl core::convert::AsRef<str>) -> core::result::Result<Self, radroots_service_host::TaskMetadataError> impl core::fmt::Display for radroots_service_host::TaskName pub fn radroots_service_host::TaskName::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result pub struct radroots_service_host::TaskSupervisor diff --git a/crates/runtime_paths/README b/crates/runtime_paths/README @@ -9,6 +9,9 @@ environment. - Validated service and instance identifiers define the sole canonical `services/<service>/<instance>` namespace. +- Bounded service, instance, and credential artifact names are validated as + borrowed UTF-8 before the crate creates their retained strings; Serde identity + decoding uses the same boundary without a second prevalidation copy. - Callers inject the platform, host-environment roots, profile, and typed bootstrap provenance. - A sealed `RuntimeContext` is the only public service-instance path diff --git a/crates/runtime_paths/src/conventions.rs b/crates/runtime_paths/src/conventions.rs @@ -25,8 +25,8 @@ pub const DEFAULT_SHARED_RUNTIME_STORE_DB_FILE_NAME: &str = "runtime_store.sqlit pub struct ServiceCredentialArtifactName(String); impl ServiceCredentialArtifactName { - pub fn new(value: impl Into<String>) -> Result<Self, ServiceCredentialArtifactNameError> { - let value = value.into(); + pub fn new(value: impl AsRef<str>) -> Result<Self, ServiceCredentialArtifactNameError> { + let value = value.as_ref(); if value.is_empty() { return Err(ServiceCredentialArtifactNameError::Empty); } @@ -46,7 +46,7 @@ impl ServiceCredentialArtifactName { { return Err(ServiceCredentialArtifactNameError::InvalidCharacter); } - Ok(Self(value)) + Ok(Self(value.to_owned())) } #[must_use] @@ -330,6 +330,12 @@ mod tests { maximum: SERVICE_CREDENTIAL_ARTIFACT_NAME_MAX_BYTES, }) ); + assert_eq!( + ServiceCredentialArtifactName::new("a".repeat(4 * 1024 * 1024)), + Err(ServiceCredentialArtifactNameError::TooLong { + maximum: SERVICE_CREDENTIAL_ARTIFACT_NAME_MAX_BYTES, + }) + ); for invalid in [ ".", "..", diff --git a/crates/runtime_paths/src/identifier.rs b/crates/runtime_paths/src/identifier.rs @@ -78,8 +78,13 @@ macro_rules! service_identity { impl $name { /// Parses and validates a canonical identifier. - pub fn new(value: impl Into<String>) -> Result<Self, ServiceIdentityError> { - let value = value.into(); + pub fn new(value: impl AsRef<str>) -> Result<Self, ServiceIdentityError> { + let value = value.as_ref(); + validate(value, $kind, $maximum)?; + Ok(Self(value.to_owned())) + } + + fn from_string(value: String) -> Result<Self, ServiceIdentityError> { validate(&value, $kind, $maximum)?; Ok(Self(value)) } @@ -121,7 +126,7 @@ macro_rules! service_identity { type Error = ServiceIdentityError; fn try_from(value: String) -> Result<Self, Self::Error> { - Self::new(value) + Self::from_string(value) } } @@ -145,8 +150,31 @@ macro_rules! service_identity { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - Self::new(value).map_err(serde::de::Error::custom) + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = $name; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a bounded canonical service identity") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + $name::new(value).map_err(E::custom) + } + + fn visit_string<E>(self, value: String) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + $name::from_string(value).map_err(E::custom) + } + } + + deserializer.deserialize_str(Visitor) } } }; @@ -212,6 +240,26 @@ mod tests { maximum: INSTANCE_ID_MAX_BYTES, }) ); + let very_large = "a".repeat(4 * 1024 * 1024); + assert!(matches!( + ServiceId::new(&very_large), + Err(ServiceIdentityError::TooLong { + kind: ServiceIdentityKind::Service, + maximum: SERVICE_ID_MAX_BYTES, + }) + )); + assert!(matches!( + InstanceId::new(&very_large), + Err(ServiceIdentityError::TooLong { + kind: ServiceIdentityKind::Instance, + maximum: INSTANCE_ID_MAX_BYTES, + }) + )); + + let service_json = serde_json::to_string(&very_large).expect("large service JSON"); + assert!(serde_json::from_str::<ServiceId>(&service_json).is_err()); + let instance_json = serde_json::to_string(&very_large).expect("large instance JSON"); + assert!(serde_json::from_str::<InstanceId>(&instance_json).is_err()); for invalid in ["Myc", "café", "a.b", "a:b", "a b", "a%b", "a/b", r"a\b"] { assert!(ServiceId::new(invalid).is_err(), "accepted `{invalid}`"); diff --git a/crates/runtime_paths/tests/package_boundary.rs b/crates/runtime_paths/tests/package_boundary.rs @@ -51,7 +51,22 @@ fn runtime_paths_is_unpublished_lint_governed_and_dependency_bounded() { for source in SOURCES { let production = source.split("#[cfg(test)]").next().unwrap_or(source); assert!(!production.contains("std::env")); + for forbidden in [ + "impl Into<String>", + "let value = value.into();", + "String::deserialize", + ] { + assert!( + !production.contains(forbidden), + "bounded runtime-path text boundary still contains `{forbidden}`" + ); + } } + assert!( + SOURCES + .iter() + .any(|source| source.contains("deserializer.deserialize_str(Visitor)")) + ); } #[test] @@ -128,6 +143,7 @@ fn reviewed_api_requires_the_typed_runtime_context_boundary() { "## Support Caveats", "## Public API Baseline", "The final reviewed root-only API", + "validated as\n borrowed UTF-8 before the crate creates their retained strings", "```rust", "| Linux `ServiceHost` | `/etc/radroots` | `/var/lib/radroots` | `/var/cache/radroots` | `/var/log/radroots` | `/run/radroots` | `/etc/radroots/secrets` |", "| Linux `InteractiveUser` | `$XDG_CONFIG_HOME/radroots` | `$XDG_DATA_HOME/radroots` | `$XDG_CACHE_HOME/radroots` | `$XDG_STATE_HOME/radroots/logs` | `$XDG_RUNTIME_DIR/radroots` | `$XDG_CONFIG_HOME/radroots/secrets` |", diff --git a/crates/service_host/README.md b/crates/service_host/README.md @@ -101,6 +101,11 @@ or streamed directly into the capped response writer; validation does not materialize an intermediate `serde_json::Value` tree. Recursive null rejection and duplicate or unknown field rejection remain fail closed. +Every bounded text constructor validates borrowed UTF-8 before it creates the +retained string. Bounded wire strings use validating Serde visitors, so the +host does not create a second prevalidation copy of identifiers, safe messages, +reason codes, task names, routes, or metric vocabulary. + ## Process and runtime ownership The crate does not parse a CLI, read configuration from environment variables, diff --git a/crates/service_host/src/admin/model.rs b/crates/service_host/src/admin/model.rs @@ -49,8 +49,19 @@ macro_rules! admin_identifier { pub struct $name(String); impl $name { - pub fn new(value: impl Into<String>) -> Result<Self, AdminIdentifierError> { - let value = value.into(); + pub fn new(value: impl AsRef<str>) -> Result<Self, AdminIdentifierError> { + let value = value.as_ref(); + let field = $field; + if value.is_empty() { + return Err(AdminIdentifierError::Empty { field }); + } + if value.len() > field.maximum_utf8_bytes() { + return Err(AdminIdentifierError::TooLong { field }); + } + Ok(Self(value.to_owned())) + } + + fn from_string(value: String) -> Result<Self, AdminIdentifierError> { let field = $field; if value.is_empty() { return Err(AdminIdentifierError::Empty { field }); @@ -87,8 +98,31 @@ macro_rules! admin_identifier { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - Self::new(value).map_err(de::Error::custom) + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = $name; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a bounded admin identifier") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + $name::new(value).map_err(E::custom) + } + + fn visit_string<E>(self, value: String) -> Result<Self::Value, E> + where + E: de::Error, + { + $name::from_string(value).map_err(E::custom) + } + } + + deserializer.deserialize_str(Visitor) } } }; @@ -116,20 +150,14 @@ impl Error for AdminErrorCodeError {} pub struct AdminErrorCode(String); impl AdminErrorCode { - pub fn new(value: impl Into<String>) -> Result<Self, AdminErrorCodeError> { - let value = value.into(); - if value.is_empty() { - return Err(AdminErrorCodeError::Empty); - } - if value.len() > ADMIN_ERROR_CODE_MAX_UTF8_BYTES { - return Err(AdminErrorCodeError::TooLong); - } - let mut bytes = value.bytes(); - if !matches!(bytes.next(), Some(b'a'..=b'z')) - || !bytes.all(|byte| matches!(byte, b'a'..=b'z' | b'0'..=b'9' | b'_')) - { - return Err(AdminErrorCodeError::InvalidCharacter); - } + pub fn new(value: impl AsRef<str>) -> Result<Self, AdminErrorCodeError> { + let value = value.as_ref(); + validate_admin_error_code(value)?; + Ok(Self(value.to_owned())) + } + + fn from_string(value: String) -> Result<Self, AdminErrorCodeError> { + validate_admin_error_code(&value)?; Ok(Self(value)) } @@ -143,6 +171,22 @@ impl AdminErrorCode { } } +fn validate_admin_error_code(value: &str) -> Result<(), AdminErrorCodeError> { + if value.is_empty() { + return Err(AdminErrorCodeError::Empty); + } + if value.len() > ADMIN_ERROR_CODE_MAX_UTF8_BYTES { + return Err(AdminErrorCodeError::TooLong); + } + let mut bytes = value.bytes(); + if !matches!(bytes.next(), Some(b'a'..=b'z')) + || !bytes.all(|byte| matches!(byte, b'a'..=b'z' | b'0'..=b'9' | b'_')) + { + return Err(AdminErrorCodeError::InvalidCharacter); + } + Ok(()) +} + impl fmt::Display for AdminErrorCode { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(self.as_str()) @@ -163,8 +207,31 @@ impl<'de> Deserialize<'de> for AdminErrorCode { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - Self::new(value).map_err(de::Error::custom) + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = AdminErrorCode; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a bounded lowercase admin error code") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + AdminErrorCode::new(value).map_err(E::custom) + } + + fn visit_string<E>(self, value: String) -> Result<Self::Value, E> + where + E: de::Error, + { + AdminErrorCode::from_string(value).map_err(E::custom) + } + } + + deserializer.deserialize_str(Visitor) } } @@ -1290,17 +1357,14 @@ fn checked_non_null(value: &impl Serialize) -> Result<(), AdminPayloadError> { pub struct AdminErrorMessage(String); impl AdminErrorMessage { - pub fn new(value: impl Into<String>) -> Result<Self, AdminErrorMessageError> { - let value = value.into(); - if value.is_empty() { - return Err(AdminErrorMessageError::Empty); - } - if value.len() > ADMIN_ERROR_MESSAGE_MAX_UTF8_BYTES { - return Err(AdminErrorMessageError::TooLong); - } - if value.chars().any(char::is_control) { - return Err(AdminErrorMessageError::ControlCharacter); - } + pub fn new(value: impl AsRef<str>) -> Result<Self, AdminErrorMessageError> { + let value = value.as_ref(); + validate_admin_error_message(value)?; + Ok(Self(value.to_owned())) + } + + fn from_string(value: String) -> Result<Self, AdminErrorMessageError> { + validate_admin_error_message(&value)?; Ok(Self(value)) } @@ -1314,6 +1378,19 @@ impl AdminErrorMessage { } } +fn validate_admin_error_message(value: &str) -> Result<(), AdminErrorMessageError> { + if value.is_empty() { + return Err(AdminErrorMessageError::Empty); + } + if value.len() > ADMIN_ERROR_MESSAGE_MAX_UTF8_BYTES { + return Err(AdminErrorMessageError::TooLong); + } + if value.chars().any(char::is_control) { + return Err(AdminErrorMessageError::ControlCharacter); + } + Ok(()) +} + impl fmt::Display for AdminErrorMessage { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str(self.as_str()) @@ -1334,8 +1411,31 @@ impl<'de> Deserialize<'de> for AdminErrorMessage { where D: Deserializer<'de>, { - let value = String::deserialize(deserializer)?; - Self::new(value).map_err(de::Error::custom) + struct Visitor; + + impl<'de> de::Visitor<'de> for Visitor { + type Value = AdminErrorMessage; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a bounded safe admin error message") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: de::Error, + { + AdminErrorMessage::new(value).map_err(E::custom) + } + + fn visit_string<E>(self, value: String) -> Result<Self::Value, E> + where + E: de::Error, + { + AdminErrorMessage::from_string(value).map_err(E::custom) + } + } + + deserializer.deserialize_str(Visitor) } } @@ -1989,6 +2089,7 @@ mod tests { assert!(AdminCorrelationId::new("é".repeat(64)).is_ok()); assert!(AdminCorrelationId::new(format!("{}x", "é".repeat(64))).is_err()); assert!(AdminErrorCode::new("valid_code_2").is_ok()); + assert!(AdminErrorCode::new("x".repeat(ADMIN_ERROR_CODE_MAX_UTF8_BYTES)).is_ok()); assert!(AdminErrorCode::new("Invalid-Code").is_err()); assert!(AdminErrorCode::new("valid-code").is_err()); assert!(AdminErrorMessage::new("x".repeat(ADMIN_ERROR_MESSAGE_MAX_UTF8_BYTES)).is_ok()); @@ -1997,6 +2098,17 @@ mod tests { ); assert!(AdminErrorMessage::new("unsafe\nmessage").is_err()); + let very_large = "x".repeat(4 * 1024 * 1024); + assert!(AdminOperationId::new(&very_large).is_err()); + assert!(AdminCorrelationId::new(&very_large).is_err()); + assert!(AdminErrorCode::new(&very_large).is_err()); + assert!(AdminErrorMessage::new(&very_large).is_err()); + let very_large_json = serde_json::to_string(&very_large).expect("large admin JSON string"); + assert!(serde_json::from_str::<AdminOperationId>(&very_large_json).is_err()); + assert!(serde_json::from_str::<AdminCorrelationId>(&very_large_json).is_err()); + assert!(serde_json::from_str::<AdminErrorCode>(&very_large_json).is_err()); + assert!(serde_json::from_str::<AdminErrorMessage>(&very_large_json).is_err()); + let wrong_success: Result<AdminSuccessResponse<ExampleResult>, _> = serde_json::from_str( r#"{"contract_version":1,"ok":false,"correlation_id":"safe-correlation","result":{"state":"committed"}}"#, ); diff --git a/crates/service_host/src/admin/server.rs b/crates/service_host/src/admin/server.rs @@ -89,8 +89,8 @@ enum AdminRouteSegment { } impl AdminRoutePath { - pub fn new(value: impl Into<String>) -> Result<Self, AdminRoutePathError> { - let value = value.into(); + pub fn new(value: impl AsRef<str>) -> Result<Self, AdminRoutePathError> { + let value = value.as_ref(); if value.is_empty() { return Err(AdminRoutePathError::Empty); } @@ -102,7 +102,6 @@ impl AdminRoutePath { } let mut parameter_names = BTreeSet::new(); let mut literal_count = 0; - let mut segments = Vec::new(); for segment in value[1..].split('/') { if segment.is_empty() { return Err(AdminRoutePathError::EmptySegment); @@ -114,10 +113,9 @@ impl AdminRoutePath { if !valid_parameter_name(parameter) { return Err(AdminRoutePathError::InvalidParameter); } - if !parameter_names.insert(parameter.to_owned()) { + if !parameter_names.insert(parameter) { return Err(AdminRoutePathError::DuplicateParameter); } - segments.push(AdminRouteSegment::Parameter(parameter.to_owned())); } else { if !segment .bytes() @@ -126,11 +124,22 @@ impl AdminRoutePath { return Err(AdminRoutePathError::InvalidCharacter); } literal_count += 1; - segments.push(AdminRouteSegment::Literal(segment.to_owned())); } } + let segments = value[1..] + .split('/') + .map(|segment| { + segment + .strip_prefix('{') + .and_then(|segment| segment.strip_suffix('}')) + .map_or_else( + || AdminRouteSegment::Literal(segment.to_owned()), + |parameter| AdminRouteSegment::Parameter(parameter.to_owned()), + ) + }) + .collect(); Ok(Self { - canonical: value, + canonical: value.to_owned(), segments, literal_count, }) @@ -587,7 +596,7 @@ impl AdminRouter { pub fn route<F, Fut>( &mut self, method: AdminHttpMethod, - path: impl Into<String>, + path: impl AsRef<str>, handler: F, ) -> Result<(), AdminRouteRegistrationError> where @@ -2153,6 +2162,16 @@ mod tests { for (path, expected) in invalid_paths { assert_eq!(AdminRoutePath::new(path).unwrap_err(), expected); } + let exact_maximum = format!( + "/v1/{}", + "x".repeat(ADMIN_ROUTE_PATH_MAX_UTF8_BYTES - "/v1/".len()) + ); + assert_eq!( + AdminRoutePath::new(&exact_maximum) + .expect("exact maximum route") + .as_str(), + exact_maximum + ); assert_eq!( AdminRoutePath::new(format!( "/v1/{}", @@ -2161,6 +2180,10 @@ mod tests { .unwrap_err(), AdminRoutePathError::TooLong ); + assert_eq!( + AdminRoutePath::new(format!("/v1/{}", "x".repeat(4 * 1024 * 1024))), + Err(AdminRoutePathError::TooLong) + ); assert_eq!( AdminHttpMethod::from_http(&Method::GET), diff --git a/crates/service_host/src/config/document.rs b/crates/service_host/src/config/document.rs @@ -23,18 +23,18 @@ pub struct ConfigDocumentExpectation { impl ConfigDocumentExpectation { /// Creates an exact expected document identity. pub fn new( - schema: impl Into<Box<str>>, + schema: impl AsRef<str>, schema_version: u32, ) -> Result<Self, ConfigDocumentExpectationError> { - let schema = schema.into(); - if !valid_schema_id(&schema) { + let schema = schema.as_ref(); + if !valid_schema_id(schema) { return Err(ConfigDocumentExpectationError::InvalidSchema); } if schema_version == 0 { return Err(ConfigDocumentExpectationError::InvalidSchemaVersion); } Ok(Self { - schema, + schema: schema.to_owned().into_boxed_str(), schema_version, }) } @@ -645,12 +645,19 @@ mod tests { ConfigDocumentExpectation::new("bad schema", 1).unwrap_err(), ConfigDocumentExpectationError::InvalidSchema ); + assert!( + ConfigDocumentExpectation::new("a".repeat(CONFIG_SCHEMA_ID_MAX_UTF8_BYTES), 1).is_ok() + ); assert_eq!( ConfigDocumentExpectation::new("a".repeat(CONFIG_SCHEMA_ID_MAX_UTF8_BYTES + 1), 1,) .unwrap_err(), ConfigDocumentExpectationError::InvalidSchema ); assert_eq!( + ConfigDocumentExpectation::new("a".repeat(4 * 1024 * 1024), 1).unwrap_err(), + ConfigDocumentExpectationError::InvalidSchema + ); + assert_eq!( ConfigDocumentExpectation::new("radroots.example.config", 0).unwrap_err(), ConfigDocumentExpectationError::InvalidSchemaVersion ); diff --git a/crates/service_host/src/config/value.rs b/crates/service_host/src/config/value.rs @@ -1,6 +1,7 @@ //! Validated service-neutral configuration leaf values. use core::fmt; +use core::marker::PhantomData; use core::str::FromStr; use core::time::Duration; use std::error::Error; @@ -107,9 +108,7 @@ impl<'de> Deserialize<'de> for PositiveDuration { where D: Deserializer<'de>, { - String::deserialize(deserializer)? - .parse() - .map_err(D::Error::custom) + deserialize_from_str(deserializer) } } @@ -209,9 +208,7 @@ impl<'de> Deserialize<'de> for ByteLimit { where D: Deserializer<'de>, { - String::deserialize(deserializer)? - .parse() - .map_err(D::Error::custom) + deserialize_from_str(deserializer) } } @@ -355,9 +352,7 @@ impl<'de> Deserialize<'de> for LoggingFormat { where D: Deserializer<'de>, { - String::deserialize(deserializer)? - .parse() - .map_err(D::Error::custom) + deserialize_from_str(deserializer) } } @@ -478,9 +473,7 @@ impl<'de> Deserialize<'de> for OptionalOperationsBind { where D: Deserializer<'de>, { - String::deserialize(deserializer)? - .parse() - .map_err(D::Error::custom) + deserialize_from_str(deserializer) } } @@ -505,6 +498,43 @@ enum HumanQuantityError { Overflow, } +fn deserialize_from_str<'de, D, T>(deserializer: D) -> Result<T, D::Error> +where + D: Deserializer<'de>, + T: FromStr, + T::Err: fmt::Display, +{ + struct FromStrVisitor<T>(PhantomData<T>); + + impl<'de, T> serde::de::Visitor<'de> for FromStrVisitor<T> + where + T: FromStr, + T::Err: fmt::Display, + { + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a canonical bounded configuration string") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + value.parse().map_err(E::custom) + } + + fn visit_string<E>(self, value: String) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + value.parse().map_err(E::custom) + } + } + + deserializer.deserialize_str(FromStrVisitor(PhantomData)) +} + fn parse_human_quantity(value: &str, units: &[(&str, u64)]) -> Result<u64, HumanQuantityError> { let Some((number, multiplier)) = units.iter().find_map(|(unit, multiplier)| { value.strip_suffix(unit).map(|number| (number, *multiplier)) diff --git a/crates/service_host/src/lifecycle/task.rs b/crates/service_host/src/lifecycle/task.rs @@ -12,12 +12,12 @@ pub const TASK_NAME_MAX_BYTES: usize = 64; pub struct TaskName(String); impl TaskName { - pub fn new(value: impl Into<String>) -> Result<Self, TaskMetadataError> { - let value = value.into(); - if !valid_task_name(&value) { + pub fn new(value: impl AsRef<str>) -> Result<Self, TaskMetadataError> { + let value = value.as_ref(); + if !valid_task_name(value) { return Err(TaskMetadataError::InvalidTaskName); } - Ok(Self(value)) + Ok(Self(value.to_owned())) } #[must_use] @@ -184,6 +184,10 @@ mod tests { ); } assert!(TaskName::new("a".repeat(TASK_NAME_MAX_BYTES + 1)).is_err()); + assert_eq!( + TaskName::new("a".repeat(4 * 1024 * 1024)), + Err(TaskMetadataError::InvalidTaskName) + ); } #[test] diff --git a/crates/service_host/src/operations/metrics.rs b/crates/service_host/src/operations/metrics.rs @@ -63,8 +63,8 @@ pub enum MetricLabelKey { pub struct MetricComponentId(String); impl MetricComponentId { - pub fn new(value: impl Into<String>) -> Result<Self, MetricsContractError> { - let value = value.into(); + pub fn new(value: impl AsRef<str>) -> Result<Self, MetricsContractError> { + let value = value.as_ref(); if value.is_empty() || value.len() > METRIC_LABEL_VALUE_MAX_BYTES || !value.bytes().enumerate().all(|(index, byte)| { @@ -77,7 +77,7 @@ impl MetricComponentId { { return Err(MetricsContractError::InvalidComponentId); } - Ok(Self(value)) + Ok(Self(value.to_owned())) } #[must_use] @@ -173,12 +173,12 @@ impl MetricLabelKey { pub struct MetricName(String); impl MetricName { - pub fn new(value: impl Into<String>) -> Result<Self, MetricsContractError> { - let value = value.into(); - if value.is_empty() || value.len() > METRIC_NAME_MAX_BYTES || !valid_metric_name(&value) { + pub fn new(value: impl AsRef<str>) -> Result<Self, MetricsContractError> { + let value = value.as_ref(); + if value.is_empty() || value.len() > METRIC_NAME_MAX_BYTES || !valid_metric_name(value) { return Err(MetricsContractError::InvalidMetricName); } - Ok(Self(value)) + Ok(Self(value.to_owned())) } #[must_use] @@ -192,8 +192,8 @@ impl MetricName { pub struct StableRelayId(String); impl StableRelayId { - pub fn new(value: impl Into<String>) -> Result<Self, MetricsContractError> { - let value = value.into(); + pub fn new(value: impl AsRef<str>) -> Result<Self, MetricsContractError> { + let value = value.as_ref(); if value.is_empty() || value.len() > STABLE_RELAY_ID_MAX_BYTES || !value.bytes().enumerate().all(|(index, byte)| { @@ -208,7 +208,7 @@ impl StableRelayId { { return Err(MetricsContractError::InvalidStableRelayId); } - Ok(Self(value)) + Ok(Self(value.to_owned())) } #[must_use] @@ -221,8 +221,8 @@ impl StableRelayId { struct MetricLabelValue(String); impl MetricLabelValue { - fn new(value: impl Into<String>) -> Result<Self, MetricsContractError> { - let value = value.into(); + fn new(value: impl AsRef<str>) -> Result<Self, MetricsContractError> { + let value = value.as_ref(); if value.is_empty() || value.len() > METRIC_LABEL_VALUE_MAX_BYTES || !value.bytes().enumerate().all(|(index, byte)| { @@ -235,7 +235,7 @@ impl MetricLabelValue { { return Err(MetricsContractError::InvalidLabelValue); } - Ok(Self(value)) + Ok(Self(value.to_owned())) } fn as_str(&self) -> &str { @@ -292,17 +292,17 @@ impl MetricLabel { #[must_use] pub fn storage(storage: MetricComponentId) -> Self { - Self::fixed(MetricLabelKey::Storage, storage.0) + Self::fixed_owned(MetricLabelKey::Storage, storage.0) } #[must_use] pub fn transport(transport: MetricComponentId) -> Self { - Self::fixed(MetricLabelKey::Transport, transport.0) + Self::fixed_owned(MetricLabelKey::Transport, transport.0) } #[must_use] pub fn relay_id(value: StableRelayId) -> Self { - Self::fixed(MetricLabelKey::RelayId, value.0) + Self::fixed_owned(MetricLabelKey::RelayId, value.0) } #[must_use] @@ -332,10 +332,17 @@ impl MetricLabel { }) } - fn fixed(key: MetricLabelKey, value: impl Into<String>) -> Self { + fn fixed(key: MetricLabelKey, value: &'static str) -> Self { Self { key, - value: MetricLabelValue(value.into()), + value: MetricLabelValue(value.to_owned()), + } + } + + fn fixed_owned(key: MetricLabelKey, value: String) -> Self { + Self { + key, + value: MetricLabelValue(value), } } } @@ -364,11 +371,11 @@ impl MetricDescriptor { pub fn new( group: CommonMetricGroup, name: MetricName, - help: impl Into<String>, + help: impl AsRef<str>, kind: MetricKind, label_keys: impl IntoIterator<Item = MetricLabelKey>, ) -> Result<Self, MetricsContractError> { - let help = help.into(); + let help = help.as_ref(); if help.is_empty() || help.len() > METRIC_HELP_MAX_BYTES || help @@ -394,7 +401,7 @@ impl MetricDescriptor { Ok(Self { group, name, - help, + help: help.to_owned(), kind, label_keys, }) @@ -1286,6 +1293,7 @@ mod tests { )); assert!(MetricName::new("_metric").is_ok()); assert!(MetricName::new(":metric9").is_ok()); + assert!(MetricName::new("a".repeat(METRIC_NAME_MAX_BYTES)).is_ok()); assert_eq!( MetricName::new("").unwrap_err(), MetricsContractError::InvalidMetricName @@ -1302,10 +1310,57 @@ mod tests { StableRelayId::new("x".repeat(STABLE_RELAY_ID_MAX_BYTES + 1)), Err(MetricsContractError::InvalidStableRelayId) ); + assert!(StableRelayId::new("x".repeat(STABLE_RELAY_ID_MAX_BYTES)).is_ok()); assert_eq!( MetricComponentId::new("x".repeat(METRIC_LABEL_VALUE_MAX_BYTES + 1)), Err(MetricsContractError::InvalidComponentId) ); + assert!(MetricComponentId::new("x".repeat(METRIC_LABEL_VALUE_MAX_BYTES)).is_ok()); + assert!(MetricLabelValue::new("x".repeat(METRIC_LABEL_VALUE_MAX_BYTES)).is_ok()); + assert!(matches!( + MetricLabelValue::new("x".repeat(METRIC_LABEL_VALUE_MAX_BYTES + 1)), + Err(MetricsContractError::InvalidLabelValue) + )); + + let exact_help = "x".repeat(METRIC_HELP_MAX_BYTES); + assert!( + MetricDescriptor::new( + CommonMetricGroup::Phase, + name("radroots_exact_help"), + &exact_help, + MetricKind::Gauge, + [MetricLabelKey::Phase], + ) + .is_ok() + ); + + let very_large = "x".repeat(4 * 1024 * 1024); + assert_eq!( + MetricName::new(&very_large), + Err(MetricsContractError::InvalidMetricName) + ); + assert_eq!( + StableRelayId::new(&very_large), + Err(MetricsContractError::InvalidStableRelayId) + ); + assert_eq!( + MetricComponentId::new(&very_large), + Err(MetricsContractError::InvalidComponentId) + ); + assert!(matches!( + MetricLabelValue::new(&very_large), + Err(MetricsContractError::InvalidLabelValue) + )); + assert_eq!( + MetricDescriptor::new( + CommonMetricGroup::Phase, + name("radroots_very_large_help"), + &very_large, + MetricKind::Gauge, + [MetricLabelKey::Phase], + ), + Err(MetricsContractError::InvalidHelp) + ); for error in [ MetricsContractError::InvalidMetricName.to_string(), diff --git a/crates/service_host/src/status/reason.rs b/crates/service_host/src/status/reason.rs @@ -51,8 +51,15 @@ impl CommonReasonCode { pub struct ReasonCode(String); impl ReasonCode { - pub fn new(value: impl Into<String>) -> Result<Self, StatusContractError> { - let value = value.into(); + pub fn new(value: impl AsRef<str>) -> Result<Self, StatusContractError> { + let value = value.as_ref(); + if !valid_reason_code(value) { + return Err(StatusContractError::InvalidReasonCode); + } + Ok(Self(value.to_owned())) + } + + fn from_string(value: String) -> Result<Self, StatusContractError> { if !valid_reason_code(&value) { return Err(StatusContractError::InvalidReasonCode); } @@ -99,7 +106,31 @@ impl<'de> Deserialize<'de> for ReasonCode { where D: Deserializer<'de>, { - Self::new(String::deserialize(deserializer)?).map_err(serde::de::Error::custom) + struct ReasonCodeVisitor; + + impl<'de> serde::de::Visitor<'de> for ReasonCodeVisitor { + type Value = ReasonCode; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a bounded canonical reason code") + } + + fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + ReasonCode::new(value).map_err(E::custom) + } + + fn visit_string<E>(self, value: String) -> Result<Self::Value, E> + where + E: serde::de::Error, + { + ReasonCode::from_string(value).map_err(E::custom) + } + } + + deserializer.deserialize_str(ReasonCodeVisitor) } } @@ -234,6 +265,14 @@ mod tests { ); } assert!(ReasonCode::new("a".repeat(REASON_CODE_MAX_BYTES + 1)).is_err()); + + let very_large = "a".repeat(4 * 1024 * 1024); + assert_eq!( + ReasonCode::new(&very_large), + Err(StatusContractError::InvalidReasonCode) + ); + let encoded = serde_json::to_string(&very_large).expect("large reason JSON"); + assert!(serde_json::from_str::<ReasonCode>(&encoded).is_err()); } #[test] diff --git a/crates/service_host/src/status/service.rs b/crates/service_host/src/status/service.rs @@ -21,12 +21,12 @@ pub const STATUS_ID_MAX_BYTES: usize = 128; pub struct StatusId(String); impl StatusId { - pub fn new(value: impl Into<String>) -> Result<Self, StatusModelError> { - let value = value.into(); - if !valid_status_id(&value) { + pub fn new(value: impl AsRef<str>) -> Result<Self, StatusModelError> { + let value = value.as_ref(); + if !valid_status_id(value) { return Err(StatusModelError::InvalidStatusId); } - Ok(Self(value)) + Ok(Self(value.to_owned())) } #[must_use] @@ -41,8 +41,8 @@ impl StatusId { pub struct Sha256Digest(String); impl Sha256Digest { - pub fn new(value: impl Into<String>) -> Result<Self, StatusModelError> { - let value = value.into(); + pub fn new(value: impl AsRef<str>) -> Result<Self, StatusModelError> { + let value = value.as_ref(); if value.len() != 64 || !value .bytes() @@ -50,7 +50,7 @@ impl Sha256Digest { { return Err(StatusModelError::InvalidSha256Digest); } - Ok(Self(value)) + Ok(Self(value.to_owned())) } #[must_use] @@ -615,8 +615,18 @@ mod tests { } assert!(StatusId::new("a".repeat(STATUS_ID_MAX_BYTES)).is_ok()); assert!(StatusId::new("a".repeat(STATUS_ID_MAX_BYTES + 1)).is_err()); + let very_large = "a".repeat(4 * 1024 * 1024); + assert_eq!( + StatusId::new(&very_large), + Err(StatusModelError::InvalidStatusId) + ); assert!(Sha256Digest::new("a".repeat(63)).is_err()); + assert!(Sha256Digest::new("a".repeat(64)).is_ok()); assert!(Sha256Digest::new("A".repeat(64)).is_err()); + assert_eq!( + Sha256Digest::new(&very_large), + Err(StatusModelError::InvalidSha256Digest) + ); let myc = ServiceId::new("myc").unwrap(); assert_eq!( validate_configuration_binding( diff --git a/crates/service_host/tests/package_boundary.rs b/crates/service_host/tests/package_boundary.rs @@ -94,6 +94,7 @@ fn service_host_is_unpublished_lint_governed_and_dependency_bounded() { for required in [ ".take(REASON_CODES_MAX_ITEMS + 1)", "deserialize_seq(ReasonCodesVisitor)", + "deserializer.deserialize_str(ReasonCodeVisitor)", ] { assert!(STATUS_SOURCE.contains(required)); } @@ -111,6 +112,26 @@ fn service_host_is_unpublished_lint_governed_and_dependency_bounded() { ] { assert!(!ADMIN_SOURCE.contains(forbidden)); } + for source in [ + CONFIG_DOCUMENT_SOURCE, + CONFIG_VALUE_SOURCE, + ADMIN_SOURCE, + STATUS_SOURCE, + LIFECYCLE_SOURCE, + OPERATIONS_PRIMITIVES_SOURCE, + ] { + for forbidden in [ + "impl Into<String>", + "impl Into<Box<str>>", + "let value = value.into();", + "String::deserialize", + ] { + assert!( + !source.contains(forbidden), + "bounded text boundary still contains `{forbidden}`" + ); + } + } for forbidden in ["tokio::signal", "ctrl_c", "signal_hook"] { assert!(!LIFECYCLE_SOURCE.contains(forbidden)); } @@ -150,6 +171,7 @@ fn documentation_and_reviewed_public_api_are_complete_and_dependency_safe() { "cached_service_state(CachedServiceState::new", "parent.child_token()", "streamed directly into the capped response writer", + "validates borrowed UTF-8 before it creates the", ] { assert!(README.contains(required), "README is missing `{required}`"); }