#![allow(missing_docs, deprecated)]
use std::collections::HashMap;
use std::path::Path;
use serde::de::{IgnoredAny, SeqAccess, Visitor};
use serde::Deserializer;
use wick_interface_types::OperationSignatures;
use wick_packet::{Entity, RuntimeConfig};
use super::template_config::Renderable;
use super::Binding;
use crate::config::{self, ExecutionSettings, ImportDefinition, LiquidJsonConfig};
use crate::error::ManifestError;
#[derive(
Debug,
Clone,
PartialEq,
derive_asset_container::AssetManager,
property::Property,
serde::Serialize,
derive_builder::Builder,
)]
#[builder(setter(into))]
#[property(get(public), set(public), mut(public, suffix = "_mut"))]
#[asset(asset(config::AssetReference))]
pub struct ComponentOperationExpression {
#[asset(skip)]
pub(crate) name: String,
pub(crate) component: ComponentDefinition,
#[asset(skip)]
#[builder(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) config: Option<LiquidJsonConfig>,
#[asset(skip)]
#[builder(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) settings: Option<ExecutionSettings>,
}
impl ComponentOperationExpression {
pub fn new_default<T: Into<String>>(operation: T, component: ComponentDefinition) -> Self {
Self {
name: operation.into(),
component,
config: Default::default(),
settings: Default::default(),
}
}
pub fn new<T: Into<String>>(
operation: T,
component: ComponentDefinition,
config: Option<LiquidJsonConfig>,
settings: Option<ExecutionSettings>,
) -> Self {
Self {
name: operation.into(),
component,
config,
settings,
}
}
pub fn maybe_import(&mut self, import_name: &str, bindings: &mut Vec<Binding<ImportDefinition>>) {
if self.component.is_reference() {
return;
}
tracing::Span::current().in_scope(|| {
tracing::debug!("importing inline component as {}", import_name);
let def = std::mem::replace(
&mut self.component,
ComponentDefinition::Reference(config::components::ComponentReference::new(import_name)),
);
bindings.push(Binding::new(import_name, config::ImportDefinition::Component(def)));
});
}
#[must_use]
pub fn as_entity(&self) -> Option<Entity> {
if let ComponentDefinition::Reference(r) = &self.component {
Some(Entity::operation(&r.id, &self.name))
} else {
None
}
}
}
impl Renderable for ComponentOperationExpression {
fn render_config(
&mut self,
source: Option<&Path>,
root_config: Option<&RuntimeConfig>,
env: Option<&HashMap<String, String>>,
) -> Result<(), ManifestError> {
if let Some(config) = self.config.as_mut() {
config.set_value(Some(config.render(source, root_config, None, env, None)?));
}
Ok(())
}
}
impl std::str::FromStr for ComponentOperationExpression {
type Err = crate::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split("::");
let operation = parts
.next()
.ok_or_else(|| crate::Error::InvalidOperationExpression(s.to_owned()))?
.to_owned();
let component = parts
.next()
.ok_or_else(|| crate::Error::InvalidOperationExpression(s.to_owned()))?
.to_owned();
Ok(Self {
name: operation,
component: ComponentDefinition::Reference(config::components::ComponentReference { id: component }),
config: Default::default(),
settings: Default::default(),
})
}
}
#[derive(Debug, Clone, PartialEq, derive_asset_container::AssetManager, serde::Serialize)]
#[asset(asset(config::AssetReference))]
#[must_use]
#[serde(rename_all = "kebab-case")]
pub enum HighLevelComponent {
#[asset(skip)]
Sql(config::components::SqlComponentConfig),
#[asset(skip)]
HttpClient(config::components::HttpClientComponentConfig),
}
impl OperationSignatures for HighLevelComponent {
fn operation_signatures(&self) -> Vec<wick_interface_types::OperationSignature> {
match self {
HighLevelComponent::Sql(c) => c.operation_signatures(),
HighLevelComponent::HttpClient(c) => c.operation_signatures(),
}
}
}
#[derive(Debug, Clone, PartialEq, derive_asset_container::AssetManager, serde::Serialize)]
#[asset(asset(config::AssetReference))]
#[must_use]
#[serde(rename_all = "kebab-case")]
pub enum ComponentDefinition {
#[doc(hidden)]
#[asset(skip)]
Native(config::components::NativeComponent),
#[deprecated(note = "Use ManifestComponent instead")]
Wasm(config::components::WasmComponent),
#[asset(skip)]
Reference(config::components::ComponentReference),
#[asset(skip)]
GrpcUrl(config::components::GrpcUrlComponent),
Manifest(config::components::ManifestComponent),
#[asset(skip)]
HighLevelComponent(HighLevelComponent),
}
#[derive(Debug, Clone, Copy)]
pub enum ComponentDefinitionKind {
Native,
Wasm,
Reference,
GrpcUrl,
Manifest,
HighLevelComponent,
}
impl std::fmt::Display for ComponentDefinitionKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ComponentDefinitionKind::Native => write!(f, "native"),
ComponentDefinitionKind::Wasm => write!(f, "wasm"),
ComponentDefinitionKind::Reference => write!(f, "reference"),
ComponentDefinitionKind::GrpcUrl => write!(f, "grpc-url"),
ComponentDefinitionKind::Manifest => write!(f, "manifest"),
ComponentDefinitionKind::HighLevelComponent => write!(f, "high-level-component"),
}
}
}
impl ComponentDefinition {
#[must_use]
pub const fn is_reference(&self) -> bool {
matches!(self, ComponentDefinition::Reference(_))
}
#[must_use]
pub const fn kind(&self) -> ComponentDefinitionKind {
match self {
ComponentDefinition::Native(_) => ComponentDefinitionKind::Native,
ComponentDefinition::Wasm(_) => ComponentDefinitionKind::Wasm,
ComponentDefinition::Reference(_) => ComponentDefinitionKind::Reference,
ComponentDefinition::GrpcUrl(_) => ComponentDefinitionKind::GrpcUrl,
ComponentDefinition::Manifest(_) => ComponentDefinitionKind::Manifest,
ComponentDefinition::HighLevelComponent(_) => ComponentDefinitionKind::HighLevelComponent,
}
}
#[must_use]
pub const fn config(&self) -> Option<&LiquidJsonConfig> {
match self {
#[allow(deprecated)]
ComponentDefinition::Wasm(c) => c.config.as_ref(),
ComponentDefinition::GrpcUrl(c) => c.config.as_ref(),
ComponentDefinition::Manifest(c) => c.config.as_ref(),
ComponentDefinition::Native(_) => None,
ComponentDefinition::Reference(_) => None,
ComponentDefinition::HighLevelComponent(_) => None,
}
}
#[must_use]
pub fn provide(&self) -> Option<&HashMap<String, String>> {
match self {
#[allow(deprecated)]
ComponentDefinition::Wasm(c) => Some(c.provide()),
ComponentDefinition::GrpcUrl(_) => None,
ComponentDefinition::Manifest(c) => Some(c.provide()),
ComponentDefinition::Native(_) => None,
ComponentDefinition::Reference(_) => None,
ComponentDefinition::HighLevelComponent(_) => None,
}
}
#[must_use]
pub fn config_mut(&mut self) -> Option<&mut LiquidJsonConfig> {
match self {
#[allow(deprecated)]
ComponentDefinition::Wasm(c) => c.config.as_mut(),
ComponentDefinition::GrpcUrl(c) => c.config.as_mut(),
ComponentDefinition::Manifest(c) => c.config.as_mut(),
ComponentDefinition::Native(_) => None,
ComponentDefinition::Reference(_) => None,
ComponentDefinition::HighLevelComponent(_) => None,
}
}
pub fn set_config(&mut self, config: Option<RuntimeConfig>) {
match self {
#[allow(deprecated)]
ComponentDefinition::Wasm(c) => c.config.as_mut().map(|c| c.set_value(config)),
ComponentDefinition::GrpcUrl(c) => c.config.as_mut().map(|c| c.set_value(config)),
ComponentDefinition::Manifest(c) => c.config.as_mut().map(|c| c.set_value(config)),
ComponentDefinition::Native(_) => None,
ComponentDefinition::Reference(_) => None,
ComponentDefinition::HighLevelComponent(_) => None,
};
}
}
impl Renderable for ComponentDefinition {
fn render_config(
&mut self,
source: Option<&Path>,
root_config: Option<&RuntimeConfig>,
env: Option<&HashMap<String, String>>,
) -> Result<(), ManifestError> {
let val = if let Some(config) = self.config() {
Some(config.render(source, root_config, None, env, None)?)
} else {
None
};
self.set_config(val);
Ok(())
}
}
impl OperationSignatures for &ComponentDefinition {
fn operation_signatures(&self) -> Vec<wick_interface_types::OperationSignature> {
match self {
ComponentDefinition::Manifest(c) => c.operation_signatures(),
ComponentDefinition::HighLevelComponent(c) => c.operation_signatures(),
ComponentDefinition::Native(_) => unreachable!(),
ComponentDefinition::Reference(_) => unreachable!(),
ComponentDefinition::GrpcUrl(_) => unreachable!(),
#[allow(deprecated)]
ComponentDefinition::Wasm(_) => unreachable!(),
}
}
}
#[derive(Default, Debug)]
struct StringPair(String, String);
impl<'de> serde::Deserialize<'de> for StringPair {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct StringPairVisitor;
impl<'de> Visitor<'de> for StringPairVisitor {
type Value = StringPair;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a String pair")
}
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
where
V: SeqAccess<'de>,
{
let s = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
let n = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
while matches!(seq.next_element()?, Some(IgnoredAny)) {
}
Ok(StringPair(s, n))
}
}
deserializer.deserialize_seq(StringPairVisitor)
}
}