use super::*;
use std::collections::BTreeMap;
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
pub struct FileVisibilityOptions {
#[serde(default)]
pub include_hidden: bool,
#[serde(default)]
pub include_ignored: bool,
}
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct SecretRef {
pub provider: String,
pub key: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
}
impl Debug for SecretRef {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("SecretRef")
.field("provider", &self.provider)
.field("key", &"[REDACTED]")
.field("version", &self.version.as_ref().map(|_| "[REDACTED]"))
.finish()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields, rename_all = "camelCase")]
pub struct WorkspaceConfig {
pub backend: String,
#[serde(default)]
pub settings: BTreeMap<String, String>,
#[serde(default)]
pub secrets: BTreeMap<String, SecretRef>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub durable_backend_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_root: Option<PathBuf>,
}
impl Default for WorkspaceConfig {
fn default() -> Self {
Self {
backend: "local".into(),
settings: BTreeMap::new(),
secrets: BTreeMap::new(),
durable_backend_id: None,
working_root: Some(std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ResolvedBackendId(pub String);
impl ResolvedBackendId {
pub fn as_str(&self) -> &str {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn secret_ref_never_debugs_secret_coordinates() {
let secret = SecretRef {
provider: "vault".into(),
key: "prod/workspace/api-token".into(),
version: Some("42".into()),
};
let debug = format!("{secret:?}");
assert!(!debug.contains("prod/workspace/api-token"));
assert!(!debug.contains("42"));
let json = serde_json::to_string(&secret).unwrap();
assert!(!json.contains("token-value"));
}
#[test]
fn provider_config_contains_references_not_plaintext_credentials() {
let json = r#"{"backend":"daytona","settings":{"apiUrl":"https://provider"},"secrets":{"credential":{"provider":"vault","key":"workspace/daytona"}}}"#;
let config: WorkspaceConfig = serde_json::from_str(json).unwrap();
assert_eq!(config.backend, "daytona");
assert_eq!(config.secrets["credential"].provider, "vault");
}
}