Skip to content

Commit

Permalink
merge upstream/main into fork/branch
Browse files Browse the repository at this point in the history
Signed-off-by: Garik Asplund <[email protected]>
  • Loading branch information
garikAsplund committed Apr 30, 2024
2 parents 9b2b26f + 5eef6b7 commit 4dcf53e
Show file tree
Hide file tree
Showing 33 changed files with 1,313 additions and 616 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ jobs:
- name: Run lints on examples
run: BUILD_SPIN_EXAMPLES=0 make lint-rust-examples

- name: Cancel everything if linting fails
if: failure()
uses: andymckay/[email protected]

## This is separated out to remove full integration tests dependencies on windows/mac builds
build-rust-ubuntu:
name: Build Spin Ubuntu
Expand Down
35 changes: 34 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

155 changes: 105 additions & 50 deletions crates/expressions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,88 @@ pub use provider::Provider;
use template::Part;
pub use template::Template;

/// A [`ProviderResolver`] that can be shared.
pub type SharedPreparedResolver =
std::sync::Arc<std::sync::OnceLock<std::sync::Arc<PreparedResolver>>>;

/// A [`Resolver`] which is extended by [`Provider`]s.
#[derive(Debug, Default)]
pub struct ProviderResolver {
internal: Resolver,
providers: Vec<Box<dyn Provider>>,
}

impl ProviderResolver {
/// Creates a Resolver for the given Tree.
pub fn new(variables: impl IntoIterator<Item = (String, Variable)>) -> Result<Self> {
Ok(Self {
internal: Resolver::new(variables)?,
providers: Default::default(),
})
}

/// Adds component variable values to the Resolver.
pub fn add_component_variables(
&mut self,
component_id: impl Into<String>,
variables: impl IntoIterator<Item = (String, String)>,
) -> Result<()> {
self.internal
.add_component_variables(component_id, variables)
}

/// Adds a variable Provider to the Resolver.
pub fn add_provider(&mut self, provider: Box<dyn Provider>) {
self.providers.push(provider);
}

/// Resolves a variable value for the given path.
pub async fn resolve(&self, component_id: &str, key: Key<'_>) -> Result<String> {
let template = self.internal.get_template(component_id, key)?;
self.resolve_template(template).await
}

/// Resolves the given template.
pub async fn resolve_template(&self, template: &Template) -> Result<String> {
let mut resolved_parts: Vec<Cow<str>> = Vec::with_capacity(template.parts().len());
for part in template.parts() {
resolved_parts.push(match part {
Part::Lit(lit) => lit.as_ref().into(),
Part::Expr(var) => self.resolve_variable(var).await?.into(),
});
}
Ok(resolved_parts.concat())
}

/// Fully resolve all variables into a [`PreparedResolver`].
pub async fn prepare(&self) -> Result<PreparedResolver> {
let mut variables = HashMap::new();
for name in self.internal.variables.keys() {
let value = self.resolve_variable(name).await?;
variables.insert(name.clone(), value);
}
Ok(PreparedResolver { variables })
}

async fn resolve_variable(&self, key: &str) -> Result<String> {
for provider in &self.providers {
if let Some(value) = provider.get(&Key(key)).await.map_err(Error::Provider)? {
return Ok(value);
}
}
self.internal.resolve_variable(key)
}
}

/// A variable resolver.
#[derive(Debug, Default)]
pub struct Resolver {
// variable key -> variable
variables: HashMap<String, Variable>,
// component ID -> variable key -> variable value template
component_configs: HashMap<String, HashMap<String, Template>>,
providers: Vec<Box<dyn Provider>>,
}

#[derive(Default)]
pub struct PreparedResolver {
variables: HashMap<String, String>,
}

pub type SharedPreparedResolver =
std::sync::Arc<std::sync::OnceLock<std::sync::Arc<PreparedResolver>>>;

impl Resolver {
/// Creates a Resolver for the given Tree.
pub fn new(variables: impl IntoIterator<Item = (String, Variable)>) -> Result<Self> {
Expand All @@ -36,7 +100,6 @@ impl Resolver {
Ok(Self {
variables,
component_configs: Default::default(),
providers: Default::default(),
})
}

Expand All @@ -62,58 +125,43 @@ impl Resolver {
Ok(())
}

/// Adds a variable Provider to the Resolver.
pub fn add_provider(&mut self, provider: Box<dyn Provider>) {
self.providers.push(provider);
}

/// Resolves a variable value for the given path.
pub async fn resolve(&self, component_id: &str, key: Key<'_>) -> Result<String> {
let configs = self.component_configs.get(component_id).ok_or_else(|| {
Error::Undefined(format!("no variable for component {component_id:?}"))
})?;

let key = key.as_ref();
let template = configs
.get(key)
.ok_or_else(|| Error::Undefined(format!("no variable for {component_id:?}.{key:?}")))?;

self.resolve_template(template).await
pub fn resolve(&self, component_id: &str, key: Key<'_>) -> Result<String> {
let template = self.get_template(component_id, key)?;
self.resolve_template(template)
}

pub async fn resolve_template(&self, template: &Template) -> Result<String> {
/// Resolves the given template.
fn resolve_template(&self, template: &Template) -> Result<String> {
let mut resolved_parts: Vec<Cow<str>> = Vec::with_capacity(template.parts().len());
for part in template.parts() {
resolved_parts.push(match part {
Part::Lit(lit) => lit.as_ref().into(),
Part::Expr(var) => self.resolve_variable(var).await?.into(),
Part::Expr(var) => self.resolve_variable(var)?.into(),
});
}
Ok(resolved_parts.concat())
}

pub async fn prepare(&self) -> Result<PreparedResolver> {
let mut variables = HashMap::new();
for name in self.variables.keys() {
let value = self.resolve_variable(name).await?;
variables.insert(name.clone(), value);
}
Ok(PreparedResolver { variables })
/// Gets a template for the given path.
fn get_template(&self, component_id: &str, key: Key<'_>) -> Result<&Template> {
let configs = self.component_configs.get(component_id).ok_or_else(|| {
Error::Undefined(format!("no variable for component {component_id:?}"))
})?;
let key = key.as_ref();
let template = configs
.get(key)
.ok_or_else(|| Error::Undefined(format!("no variable for {component_id:?}.{key:?}")))?;
Ok(template)
}

async fn resolve_variable(&self, key: &str) -> Result<String> {
fn resolve_variable(&self, key: &str) -> Result<String> {
let var = self
.variables
.get(key)
// This should have been caught by validate_template
.ok_or_else(|| Error::InvalidName(key.to_string()))?;

for provider in &self.providers {
if let Some(value) = provider.get(&Key(key)).await.map_err(Error::Provider)? {
return Ok(value);
}
}

var.default.clone().ok_or_else(|| {
Error::Provider(anyhow::anyhow!(
"no provider resolved required variable {key:?}"
Expand All @@ -134,14 +182,14 @@ impl Resolver {
}
}

impl PreparedResolver {
fn resolve_variable(&self, key: &str) -> Result<String> {
self.variables
.get(key)
.cloned()
.ok_or(Error::InvalidName(key.to_string()))
}
/// A resolver who has resolved all variables.
#[derive(Default)]
pub struct PreparedResolver {
variables: HashMap<String, String>,
}

impl PreparedResolver {
/// Resolves a the given template.
pub fn resolve_template(&self, template: &Template) -> Result<String> {
let mut resolved_parts: Vec<Cow<str>> = Vec::with_capacity(template.parts().len());
for part in template.parts() {
Expand All @@ -152,6 +200,13 @@ impl PreparedResolver {
}
Ok(resolved_parts.concat())
}

fn resolve_variable(&self, key: &str) -> Result<String> {
self.variables
.get(key)
.cloned()
.ok_or(Error::InvalidName(key.to_string()))
}
}

/// A variable key
Expand Down Expand Up @@ -245,7 +300,7 @@ mod tests {
}

async fn test_resolve(template: &str) -> Result<String> {
let mut resolver = Resolver::new([
let mut resolver = ProviderResolver::new([
(
"required".into(),
Variable {
Expand Down
1 change: 1 addition & 0 deletions crates/http/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ http-body-util = { workspace = true }
wasmtime-wasi-http = { workspace = true, optional = true }
indexmap = "1"
percent-encoding = "2"
routefinder = "0.5.4"
serde = { version = "1.0", features = ["derive"] }
tracing = { workspace = true }
spin-app = { path = "../app", optional = true }
Expand Down
Loading

0 comments on commit 4dcf53e

Please sign in to comment.