1#![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
22pub type ProviderId = Symbol;
24
25pub type ServiceId = Symbol;
27
28#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct DeclaredLimit {
31 pub resource: Symbol,
33 pub maximum: u64,
35}
36
37#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct SanitizedProvenance {
40 pub provider: ProviderId,
42 pub service: ServiceId,
44 pub revision: Option<Symbol>,
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub struct HostPortCard {
51 pub service: ServiceId,
53 pub limits: Vec<DeclaredLimit>,
55 pub provenance: SanitizedProvenance,
57}
58
59#[derive(Clone, Debug, PartialEq, Eq)]
61pub enum HostRefusal {
62 Unsupported,
64 Denied,
66 Unavailable,
68 Suspended,
70 Invalid,
72 BudgetExhausted,
74 Cancelled,
76 ProviderFault,
78}
79
80pub type HostResult<T> = core::result::Result<T, HostRefusal>;
82
83pub trait HostPort: RuntimeObject {
89 fn host_port_card(&self) -> &HostPortCard;
91}
92
93pub 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
112pub 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}