use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::vec::Vec;
#[cfg(feature = "schemars")]
use schemars::JsonSchema;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{
ArtifactRef, BundleId, CollectionId, ComponentRef, DistributorRef, EnvironmentRef,
MetadataRecordRef, PackId, PackRef, SemverReq, StoreFrontId, StorePlanId, StoreProductId,
SubscriptionId, TenantCtx,
};
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct Theme {
pub primary_color: String,
pub secondary_color: String,
pub accent_color: String,
pub background_color: String,
pub text_color: String,
pub font_family: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub logo_url: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub favicon_url: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub hero_image_url: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub hero_title: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub hero_subtitle: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub card_radius: Option<u8>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub card_elevation: Option<u8>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub button_style: Option<String>,
}
impl Default for Theme {
fn default() -> Self {
Self {
primary_color: "#0f766e".into(),
secondary_color: "#134e4a".into(),
accent_color: "#10b981".into(),
background_color: "#ffffff".into(),
text_color: "#0f172a".into(),
font_family: "Inter, sans-serif".into(),
logo_url: None,
favicon_url: None,
hero_image_url: None,
hero_title: None,
hero_subtitle: None,
card_radius: None,
card_elevation: None,
button_style: None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum LayoutSectionKind {
Hero,
FeaturedCollection,
Grid,
Cta,
Custom(String),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct LayoutSection {
pub id: String,
pub kind: LayoutSectionKind,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub collection_id: Option<CollectionId>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub title: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub subtitle: Option<String>,
pub sort_order: i32,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct Collection {
pub id: CollectionId,
pub storefront_id: StoreFrontId,
pub title: String,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub product_ids: Vec<StoreProductId>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub slug: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub description: Option<String>,
pub sort_order: i32,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct ProductOverride {
pub storefront_id: StoreFrontId,
pub product_id: StoreProductId,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub display_name: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub short_description: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub badges: Vec<String>,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct StoreFront {
pub id: StoreFrontId,
pub slug: String,
pub name: String,
#[cfg_attr(feature = "serde", serde(default))]
pub theme: Theme,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub sections: Vec<LayoutSection>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub collections: Vec<Collection>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub overrides: Vec<ProductOverride>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub worker_id: Option<String>,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum StoreProductKind {
Component,
Flow,
Pack,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum VersionStrategy {
Latest,
Pinned {
requirement: SemverReq,
},
Lts,
Custom(String),
Fixed {
version: String,
},
Range {
range: String,
},
Channel {
channel: String,
},
CustomTagged {
value: String,
},
}
#[cfg(feature = "serde")]
impl Serialize for VersionStrategy {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
#[derive(Serialize)]
#[serde(rename_all = "snake_case")]
enum Legacy<'a> {
Latest,
Pinned { requirement: &'a SemverReq },
Lts,
Custom(&'a str),
}
#[derive(Serialize)]
struct Tagged<'a> {
#[serde(rename = "kind")]
kind: &'static str,
#[serde(skip_serializing_if = "Option::is_none")]
version: Option<&'a String>,
#[serde(skip_serializing_if = "Option::is_none")]
range: Option<&'a String>,
#[serde(skip_serializing_if = "Option::is_none")]
channel: Option<&'a String>,
#[serde(skip_serializing_if = "Option::is_none")]
value: Option<&'a String>,
}
match self {
VersionStrategy::Latest => Legacy::Latest.serialize(serializer),
VersionStrategy::Pinned { requirement } => {
Legacy::Pinned { requirement }.serialize(serializer)
}
VersionStrategy::Lts => Legacy::Lts.serialize(serializer),
VersionStrategy::Custom(value) => Legacy::Custom(value).serialize(serializer),
VersionStrategy::Fixed { version } => Tagged {
kind: "fixed",
version: Some(version),
range: None,
channel: None,
value: None,
}
.serialize(serializer),
VersionStrategy::Range { range } => Tagged {
kind: "range",
version: None,
range: Some(range),
channel: None,
value: None,
}
.serialize(serializer),
VersionStrategy::Channel { channel } => Tagged {
kind: "channel",
version: None,
range: None,
channel: Some(channel),
value: None,
}
.serialize(serializer),
VersionStrategy::CustomTagged { value } => Tagged {
kind: "custom",
version: None,
range: None,
channel: None,
value: Some(value),
}
.serialize(serializer),
}
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for VersionStrategy {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(rename_all = "snake_case")]
enum Legacy {
Latest,
Pinned { requirement: SemverReq },
Lts,
Custom(String),
}
#[derive(Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
enum Tagged {
Latest,
Fixed { version: String },
Range { range: String },
Channel { channel: String },
Custom { value: String },
}
#[derive(Deserialize)]
#[serde(untagged)]
enum Wrapper {
Tagged(Tagged),
Legacy(Legacy),
}
match Wrapper::deserialize(deserializer)? {
Wrapper::Tagged(tagged) => match tagged {
Tagged::Latest => Ok(VersionStrategy::Latest),
Tagged::Fixed { version } => Ok(VersionStrategy::Fixed { version }),
Tagged::Range { range } => Ok(VersionStrategy::Range { range }),
Tagged::Channel { channel } => Ok(VersionStrategy::Channel { channel }),
Tagged::Custom { value } => Ok(VersionStrategy::CustomTagged { value }),
},
Wrapper::Legacy(legacy) => match legacy {
Legacy::Latest => Ok(VersionStrategy::Latest),
Legacy::Pinned { requirement } => Ok(VersionStrategy::Pinned { requirement }),
Legacy::Lts => Ok(VersionStrategy::Lts),
Legacy::Custom(value) => Ok(VersionStrategy::Custom(value)),
},
}
}
}
pub type CapabilityMap = BTreeMap<String, Vec<String>>;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct StoreProduct {
pub id: StoreProductId,
pub kind: StoreProductKind,
pub name: String,
pub slug: String,
pub description: String,
pub source_repo: crate::RepoRef,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub component_ref: Option<ComponentRef>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub pack_ref: Option<PackId>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub category: Option<String>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub tags: Vec<String>,
#[cfg_attr(feature = "serde", serde(default))]
pub capabilities: CapabilityMap,
pub version_strategy: VersionStrategy,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub default_plan_id: Option<StorePlanId>,
pub is_free: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum PriceModel {
Free,
Flat {
amount_micro: u64,
period_days: u16,
},
Metered {
included_units: u64,
overage_rate_micro: u64,
unit_label: String,
},
Enterprise {
description: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct PlanLimits {
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub max_environments: Option<u32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub max_subscriptions: Option<u32>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub monthly_units_included: Option<u64>,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct StorePlan {
pub id: StorePlanId,
pub name: String,
pub description: String,
pub price_model: PriceModel,
#[cfg_attr(feature = "serde", serde(default))]
pub limits: PlanLimits,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub tags: Vec<String>,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum SubscriptionStatus {
Draft,
Active,
Paused,
Cancelled,
Error,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct Subscription {
pub id: SubscriptionId,
pub tenant_ctx: TenantCtx,
pub product_id: StoreProductId,
pub plan_id: StorePlanId,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub environment_ref: Option<EnvironmentRef>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub distributor_ref: Option<DistributorRef>,
pub status: SubscriptionStatus,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum PackOrComponentRef {
Component(ComponentRef),
Pack(PackId),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum ArtifactSelector {
Component(ComponentRef),
Pack(PackRef),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct DesiredSubscriptionEntry {
pub selector: ArtifactSelector,
pub version_strategy: VersionStrategy,
#[cfg_attr(feature = "serde", serde(default))]
pub config_overrides: BTreeMap<String, Value>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub policy_tags: Vec<String>,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct DesiredState {
pub tenant: TenantCtx,
pub environment_ref: EnvironmentRef,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub entries: Vec<DesiredSubscriptionEntry>,
pub version: u64,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum ConnectionKind {
Online,
Offline,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct Environment {
pub id: EnvironmentRef,
pub tenant: TenantCtx,
pub name: String,
#[cfg_attr(feature = "serde", serde(default))]
pub labels: BTreeMap<String, String>,
pub distributor_ref: DistributorRef,
pub connection_kind: ConnectionKind,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
impl Environment {
pub fn new(
id: EnvironmentRef,
tenant: TenantCtx,
distributor_ref: DistributorRef,
connection_kind: ConnectionKind,
name: impl Into<String>,
) -> Self {
Self {
id,
tenant,
name: name.into(),
distributor_ref,
connection_kind,
labels: BTreeMap::new(),
metadata: BTreeMap::new(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub enum RolloutState {
Pending,
Planning,
InProgress,
Succeeded,
Failed,
Blocked,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct RolloutStatus {
pub environment_ref: EnvironmentRef,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub desired_state_version: Option<u64>,
pub state: RolloutState,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub bundle_id: Option<BundleId>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Option::is_none")
)]
pub message: Option<String>,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct BundleSpec {
pub bundle_id: BundleId,
pub tenant: TenantCtx,
pub environment_ref: EnvironmentRef,
pub desired_state_version: u64,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub artifact_refs: Vec<ArtifactRef>,
#[cfg_attr(
feature = "serde",
serde(default, skip_serializing_if = "Vec::is_empty")
)]
pub metadata_refs: Vec<MetadataRecordRef>,
#[cfg_attr(feature = "serde", serde(default))]
pub additional_metadata: BTreeMap<String, Value>,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "schemars", derive(JsonSchema))]
pub struct DesiredStateExportSpec {
pub tenant: TenantCtx,
pub environment_ref: EnvironmentRef,
pub desired_state_version: u64,
#[cfg_attr(feature = "serde", serde(default))]
pub include_artifacts: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub include_metadata: bool,
#[cfg_attr(feature = "serde", serde(default))]
pub metadata: BTreeMap<String, Value>,
}