use std::collections::BTreeSet;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::identity::{HostOs, Provider};
use crate::list::{NonEmpty, StepSelection};
use crate::names::{EnvName, Identifier, Name, SnakeName, StepId};
use crate::path::RelPath;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum SetBy {
Runner,
Embedder,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum TfVarShape {
#[default]
String,
MapEntry,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum StateFileFormat {
TfvarsJson,
Dotenv,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Transport {
Ssh,
VagrantSsh,
Winrm,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "via", rename_all = "snake_case", deny_unknown_fields)]
pub enum Delivery {
Env {
var: EnvName,
steps: StepSelection,
},
TfVar {
var: SnakeName,
#[serde(default, skip_serializing_if = "crate::step::is_default")]
shape: TfVarShape,
},
StateFile {
path: RelPath,
format: StateFileFormat,
#[serde(default, skip_serializing_if = "Option::is_none")]
key: Option<String>,
},
Guest {
machine: Name,
transport: Transport,
#[serde(default, skip_serializing_if = "Option::is_none")]
during_step: Option<StepId>,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Param {
pub name: EnvName,
pub description: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub values: Vec<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub required: bool,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub hosts: BTreeSet<HostOs>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub providers: BTreeSet<Provider>,
pub set_by: SetBy,
pub deliver: NonEmpty<Delivery>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum SlotKind {
Input,
Secret,
Connection,
Generated,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum Store {
Env,
File,
Vault,
Sops,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SecretSource {
pub store: Store,
pub reference: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SecretSlot {
pub name: Identifier,
pub kind: SlotKind,
pub required: bool,
pub sensitive: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub machines: Vec<Name>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub providers: BTreeSet<Provider>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source: Option<SecretSource>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<String>,
pub deliver: NonEmpty<Delivery>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_delivery_is_tagged_by_its_way() {
let env: Delivery = serde_json::from_str(r#"{"via": "env", "var": "CTF_FLAG_MAIN", "steps": ["up"]}"#).unwrap();
assert!(matches!(env, Delivery::Env { .. }));
let tf: Delivery = serde_json::from_str(r#"{"via": "tf_var", "var": "inputs", "shape": "map_entry"}"#).unwrap();
assert!(matches!(
tf,
Delivery::TfVar {
shape: TfVarShape::MapEntry,
..
}
));
assert_eq!(
serde_json::to_string(&Delivery::TfVar {
var: SnakeName::parse("region").unwrap(),
shape: TfVarShape::String
})
.unwrap(),
r#"{"via":"tf_var","var":"region"}"#
);
assert_eq!(
serde_json::to_string(&Delivery::TfVar {
var: SnakeName::parse("inputs").unwrap(),
shape: TfVarShape::MapEntry
})
.unwrap(),
r#"{"via":"tf_var","var":"inputs","shape":"map_entry"}"#
);
assert!(serde_json::from_str::<Delivery>(r#"{"via": "argv", "var": "X"}"#).is_err());
assert!(serde_json::from_str::<Delivery>(r#"{"via": "env", "var": "X", "steps": ["*"], "value": "secret"}"#).is_err());
}
}