use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use crate::infra::execution::placement::ProjectRuntimeProfile;
pub const PROJECT_RUNTIME_BUNDLE_SCHEMA_VERSION: u32 = 1;
pub const SECRET_BINDINGS_MANIFEST_SCHEMA_VERSION: u32 = 1;
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ProjectBundleIdentity {
pub owner: String,
pub project: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub title: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub revision: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ProjectBootstrapPlan {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub activate: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct ProjectBundleContents {
#[serde(default = "default_true")]
pub include_repo: bool,
#[serde(default)]
pub include_files_public: bool,
#[serde(default)]
pub include_files_private: bool,
#[serde(default)]
pub include_runtime_snapshot: bool,
}
impl Default for ProjectBundleContents {
fn default() -> Self {
Self {
include_repo: true,
include_files_public: false,
include_files_private: false,
include_runtime_snapshot: false,
}
}
}
const fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ProjectBundleFile {
pub path: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub data_base64: String,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum SecretBindingKind {
Credential,
DbConnection,
Environment,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct SecretBindingRequirement {
pub binding_id: String,
pub kind: SecretBindingKind,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub kind_hint: String,
#[serde(default)]
pub required: bool,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub description: String,
}
impl Default for SecretBindingKind {
fn default() -> Self {
Self::Credential
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct SecretBindingsManifest {
#[serde(default = "default_secret_bindings_manifest_schema_version")]
pub schema_version: u32,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub bindings: Vec<SecretBindingRequirement>,
}
const fn default_secret_bindings_manifest_schema_version() -> u32 {
SECRET_BINDINGS_MANIFEST_SCHEMA_VERSION
}
impl Default for SecretBindingsManifest {
fn default() -> Self {
Self {
schema_version: SECRET_BINDINGS_MANIFEST_SCHEMA_VERSION,
bindings: Vec::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ProjectRuntimeBundle {
#[serde(default = "default_project_runtime_bundle_schema_version")]
pub schema_version: u32,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub bundle_id: String,
#[serde(default)]
pub identity: ProjectBundleIdentity,
#[serde(default)]
pub created_at: i64,
#[serde(default)]
pub runtime_profile: ProjectRuntimeProfile,
#[serde(default)]
pub bootstrap: ProjectBootstrapPlan,
#[serde(default)]
pub contents: ProjectBundleContents,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub repo_files: Vec<ProjectBundleFile>,
#[serde(default)]
pub secret_bindings: SecretBindingsManifest,
#[serde(default, skip_serializing_if = "Value::is_null")]
pub metadata: Value,
}
const fn default_project_runtime_bundle_schema_version() -> u32 {
PROJECT_RUNTIME_BUNDLE_SCHEMA_VERSION
}
impl Default for ProjectRuntimeBundle {
fn default() -> Self {
Self {
schema_version: PROJECT_RUNTIME_BUNDLE_SCHEMA_VERSION,
bundle_id: String::new(),
identity: ProjectBundleIdentity::default(),
created_at: 0,
runtime_profile: ProjectRuntimeProfile::default(),
bootstrap: ProjectBootstrapPlan::default(),
contents: ProjectBundleContents::default(),
repo_files: Vec::new(),
secret_bindings: SecretBindingsManifest::default(),
metadata: Value::Object(Map::new()),
}
}
}
#[cfg(test)]
mod tests {
use super::{
PROJECT_RUNTIME_BUNDLE_SCHEMA_VERSION, ProjectBundleContents, ProjectRuntimeBundle,
SECRET_BINDINGS_MANIFEST_SCHEMA_VERSION, SecretBindingsManifest,
};
#[test]
fn defaults_are_versioned_and_portable() {
let bundle = ProjectRuntimeBundle::default();
assert_eq!(bundle.schema_version, PROJECT_RUNTIME_BUNDLE_SCHEMA_VERSION);
assert!(bundle.contents.include_repo);
assert!(!bundle.contents.include_runtime_snapshot);
let manifest = SecretBindingsManifest::default();
assert_eq!(
manifest.schema_version,
SECRET_BINDINGS_MANIFEST_SCHEMA_VERSION
);
}
#[test]
fn repo_only_bundle_is_the_default_contents_shape() {
let contents = ProjectBundleContents::default();
assert!(contents.include_repo);
assert!(!contents.include_files_public);
assert!(!contents.include_files_private);
assert!(!contents.include_runtime_snapshot);
}
}