use alloc::collections::{BTreeMap, BTreeSet};
use alloc::{format, string::String, vec::Vec};
#[cfg(feature = "schemars")]
use schemars::JsonSchema;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{ErrorCode, GResult, GreenticError};
pub const PROVIDER_EXTENSION_ID: &str = "greentic.provider-extension.v1";
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ProviderManifest {
pub provider_type: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub capabilities: Vec<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub ops: Vec<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub config_schema_ref: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub state_schema_ref: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ProviderRuntimeRef {
pub component_ref: String,
pub export: String,
pub world: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ProviderDecl {
pub provider_type: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub capabilities: Vec<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub ops: Vec<String>,
pub config_schema_ref: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub state_schema_ref: Option<String>,
pub runtime: ProviderRuntimeRef,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub docs_ref: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ProviderExtensionInline {
pub providers: Vec<ProviderDecl>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "BTreeMap::is_empty", flatten)
)]
pub additional_fields: BTreeMap<String, Value>,
}
impl ProviderExtensionInline {
pub fn validate_basic(&self) -> GResult<()> {
let mut seen = BTreeSet::new();
for provider in &self.providers {
if provider.provider_type.is_empty() {
return Err(GreenticError::new(
ErrorCode::InvalidInput,
"ProviderDecl.provider_type must not be empty",
));
}
if !seen.insert(&provider.provider_type) {
return Err(GreenticError::new(
ErrorCode::InvalidInput,
format!(
"duplicate provider_type '{}' in ProviderExtensionInline",
provider.provider_type
),
));
}
if provider.runtime.component_ref.trim().is_empty()
|| provider.runtime.export.trim().is_empty()
|| provider.runtime.world.trim().is_empty()
{
return Err(GreenticError::new(
ErrorCode::InvalidInput,
format!(
"runtime fields must be set for provider '{}'",
provider.provider_type
),
));
}
}
Ok(())
}
}