Skip to main content

sim_host_core/
lib.rs

1//! Neutral contracts for domain-defined host ports.
2//!
3//! This crate describes a port without realizing one. Provider selection,
4//! evidence grading, operating-system integration, and product policy belong
5//! in platform and domain libraries. A domain implements [`HostPort`] on its
6//! own opaque runtime object and installs that object in a lexical child
7//! [`Env`] with [`bind_host_port`].
8
9#![forbid(unsafe_code)]
10#![deny(missing_docs)]
11
12use std::sync::Arc;
13
14use sim_kernel::{Env, Factory, RuntimeObject, Symbol, Value, error::Result};
15
16mod time;
17pub use time::{
18    DeterministicTime, MonotonicClock, MonotonicTimestamp, PlatformTime, SystemWallClock, Timer,
19    WallClock, WallTimestamp,
20};
21
22/// An open provider identity, represented as kernel data rather than an enum.
23pub type ProviderId = Symbol;
24
25/// An open service identity, represented as kernel data rather than an enum.
26pub type ServiceId = Symbol;
27
28/// A declared, mechanically enforced resource limit.
29#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct DeclaredLimit {
31    /// Open resource name, such as `bytes/request` or `calls/second`.
32    pub resource: Symbol,
33    /// Maximum amount of the resource admitted by the port.
34    pub maximum: u64,
35}
36
37/// Non-secret provenance safe to expose on a host-port card.
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct SanitizedProvenance {
40    /// Provider that supplied the port.
41    pub provider: ProviderId,
42    /// Service realized by the port.
43    pub service: ServiceId,
44    /// Optional provider-defined revision or deployment label.
45    pub revision: Option<Symbol>,
46}
47
48/// Dependency-light descriptive data published by a host port.
49#[derive(Clone, Debug, PartialEq, Eq)]
50pub struct HostPortCard {
51    /// Stable service identity offered by this port.
52    pub service: ServiceId,
53    /// Mechanical resource limits declared by the provider.
54    pub limits: Vec<DeclaredLimit>,
55    /// Sanitized provider provenance; credentials and host details never belong here.
56    pub provenance: SanitizedProvenance,
57}
58
59/// Common host-call refusals limited to runtime mechanics.
60#[derive(Clone, Debug, PartialEq, Eq)]
61pub enum HostRefusal {
62    /// The provider does not implement the requested operation.
63    Unsupported,
64    /// The call lacks authority granted by the realizing layer.
65    Denied,
66    /// The provider is not currently reachable or ready.
67    Unavailable,
68    /// The port has been intentionally paused.
69    Suspended,
70    /// The request is malformed for the provider contract.
71    Invalid,
72    /// A declared mechanical budget has been consumed.
73    BudgetExhausted,
74    /// The caller or runtime cancelled the operation.
75    Cancelled,
76    /// The provider failed without exposing unsafe internal detail.
77    ProviderFault,
78}
79
80/// Result returned by neutral host-port operations.
81pub type HostResult<T> = core::result::Result<T, HostRefusal>;
82
83/// Marker contract implemented by a domain's opaque host-port object.
84///
85/// The trait deliberately specifies only descriptive identity. Domain methods
86/// remain on domain traits, so this foundation cannot accumulate platform or
87/// product policy.
88pub trait HostPort: RuntimeObject {
89    /// Returns the neutral card describing this port.
90    fn host_port_card(&self) -> &HostPortCard;
91}
92
93/// Creates a child environment and binds an opaque domain port in its local frame.
94///
95/// No process-global registry is involved; dropping the environment drops its
96/// ownership of the binding.
97pub fn bind_host_port<P>(
98    factory: &dyn Factory,
99    parent: Arc<Env>,
100    binding: Symbol,
101    port: Arc<P>,
102) -> Result<Env>
103where
104    P: HostPort + 'static,
105{
106    let mut child = Env::child(parent);
107    let opaque: Arc<dyn RuntimeObject> = port;
108    child.define(binding, factory.opaque(opaque)?);
109    Ok(child)
110}
111
112/// Looks up the opaque value bound for a domain host port.
113pub fn host_port_value(env: &Env, binding: &Symbol) -> Option<Value> {
114    env.get(binding)
115}
116
117#[cfg(test)]
118mod tests {
119    use std::{any::Any, sync::Arc};
120
121    use sim_kernel::{Cx, DefaultFactory, Object, ObjectCompat};
122
123    use super::*;
124
125    struct FictionalPort {
126        card: HostPortCard,
127    }
128
129    impl Object for FictionalPort {
130        fn display(&self, _cx: &mut Cx) -> Result<String> {
131            Ok("#<fictional-host-port>".into())
132        }
133
134        fn as_any(&self) -> &dyn Any {
135            self
136        }
137    }
138
139    impl ObjectCompat for FictionalPort {}
140
141    impl HostPort for FictionalPort {
142        fn host_port_card(&self) -> &HostPortCard {
143            &self.card
144        }
145    }
146
147    #[test]
148    fn fictional_open_ids_bind_as_an_opaque_child_value() {
149        let provider = Symbol::qualified("fictional-provider", "orbital");
150        let service = Symbol::qualified("fictional-service", "weather-on-mars");
151        let port = Arc::new(FictionalPort {
152            card: HostPortCard {
153                service: service.clone(),
154                limits: vec![DeclaredLimit {
155                    resource: Symbol::qualified("calls", "request"),
156                    maximum: 7,
157                }],
158                provenance: SanitizedProvenance {
159                    provider: provider.clone(),
160                    service,
161                    revision: Some(Symbol::new("prototype-9")),
162                },
163            },
164        });
165        let binding = Symbol::qualified("host-port", "weather");
166        let parent = Arc::new(Env::default());
167        let child = bind_host_port(&DefaultFactory, parent.clone(), binding.clone(), port)
168            .expect("opaque binding");
169
170        let value = host_port_value(&child, &binding).expect("local port");
171        let recovered = value
172            .object()
173            .downcast_ref::<FictionalPort>()
174            .expect("domain type remains recoverable");
175        assert_eq!(recovered.card.provenance.provider, provider);
176        assert!(parent.get(&binding).is_none());
177    }
178
179    #[test]
180    fn common_refusal_vocabulary_is_exhaustive_and_mechanical() {
181        let refusals = [
182            HostRefusal::Unsupported,
183            HostRefusal::Denied,
184            HostRefusal::Unavailable,
185            HostRefusal::Suspended,
186            HostRefusal::Invalid,
187            HostRefusal::BudgetExhausted,
188            HostRefusal::Cancelled,
189            HostRefusal::ProviderFault,
190        ];
191        assert_eq!(refusals.len(), 8);
192    }
193}