-
Notifications
You must be signed in to change notification settings - Fork 248
/
host_component.rs
122 lines (107 loc) · 3.81 KB
/
host_component.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::sync::{Arc, Mutex};
use anyhow::Result;
use once_cell::sync::OnceCell;
use spin_app::{AppComponent, DynamicHostComponent};
use spin_core::{async_trait, HostComponent};
use spin_world::v1::config::Error as V1ConfigError;
use spin_world::v2::variables;
use spin_expressions::{Error, Key, Provider, ProviderResolver};
pub struct VariablesHostComponent {
providers: Mutex<Vec<Box<dyn Provider>>>,
resolver: Arc<OnceCell<ProviderResolver>>,
}
impl VariablesHostComponent {
pub fn new(providers: Vec<Box<dyn Provider>>) -> Self {
Self {
providers: Mutex::new(providers),
resolver: Default::default(),
}
}
}
impl HostComponent for VariablesHostComponent {
type Data = ComponentVariables;
fn add_to_linker<T: Send>(
linker: &mut spin_core::Linker<T>,
get: impl Fn(&mut spin_core::Data<T>) -> &mut Self::Data + Send + Sync + Copy + 'static,
) -> anyhow::Result<()> {
spin_world::v1::config::add_to_linker(linker, get)?;
variables::add_to_linker(linker, get)
}
fn build_data(&self) -> Self::Data {
ComponentVariables {
resolver: self.resolver.clone(),
component_id: None,
}
}
}
impl DynamicHostComponent for VariablesHostComponent {
fn update_data(&self, data: &mut Self::Data, component: &AppComponent) -> anyhow::Result<()> {
self.resolver.get_or_try_init(|| {
make_resolver(component.app, self.providers.lock().unwrap().drain(..))
})?;
data.component_id = Some(component.id().to_string());
Ok(())
}
}
pub fn make_resolver(
app: &spin_app::App,
providers: impl IntoIterator<Item = Box<dyn Provider>>,
) -> anyhow::Result<ProviderResolver> {
let mut resolver =
ProviderResolver::new(app.variables().map(|(key, var)| (key.clone(), var.clone())))?;
for component in app.components() {
resolver.add_component_variables(
component.id(),
component.config().map(|(k, v)| (k.into(), v.into())),
)?;
}
for provider in providers {
resolver.add_provider(provider);
}
Ok(resolver)
}
/// A component variables interface implementation.
pub struct ComponentVariables {
resolver: Arc<OnceCell<ProviderResolver>>,
component_id: Option<String>,
}
#[async_trait]
impl variables::Host for ComponentVariables {
async fn get(&mut self, key: String) -> Result<String, variables::Error> {
// Set by DynamicHostComponent::update_data
let component_id = self.component_id.as_deref().unwrap();
let key = Key::new(&key).map_err(as_wit)?;
self.resolver
.get()
.unwrap()
.resolve(component_id, key)
.await
.map_err(as_wit)
}
fn convert_error(&mut self, error: variables::Error) -> Result<variables::Error> {
Ok(error)
}
}
#[async_trait]
impl spin_world::v1::config::Host for ComponentVariables {
async fn get_config(&mut self, key: String) -> Result<String, V1ConfigError> {
<Self as variables::Host>::get(self, key)
.await
.map_err(|err| match err {
variables::Error::InvalidName(msg) => V1ConfigError::InvalidKey(msg),
variables::Error::Undefined(msg) => V1ConfigError::Provider(msg),
other => V1ConfigError::Other(format!("{other}")),
})
}
fn convert_error(&mut self, error: V1ConfigError) -> Result<V1ConfigError> {
Ok(error)
}
}
fn as_wit(err: Error) -> variables::Error {
match err {
Error::InvalidName(msg) => variables::Error::InvalidName(msg),
Error::Undefined(msg) => variables::Error::Undefined(msg),
Error::Provider(err) => variables::Error::Provider(err.to_string()),
other => variables::Error::Other(format!("{other}")),
}
}