use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DockerLayerType {
Base,
Stack,
Agent,
Project,
}
impl fmt::Display for DockerLayerType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DockerLayerType::Base => write!(f, "base"),
DockerLayerType::Stack => write!(f, "stack"),
DockerLayerType::Agent => write!(f, "agent"),
DockerLayerType::Project => write!(f, "project"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DockerLayer {
pub layer_type: DockerLayerType,
pub name: String,
}
impl DockerLayer {
pub fn base() -> Self {
Self::base_with_name("default")
}
pub fn base_with_name(name: impl Into<String>) -> Self {
Self {
layer_type: DockerLayerType::Base,
name: name.into(),
}
}
pub fn stack(name: impl Into<String>) -> Self {
Self {
layer_type: DockerLayerType::Stack,
name: name.into(),
}
}
pub fn agent(name: impl Into<String>) -> Self {
Self {
layer_type: DockerLayerType::Agent,
name: name.into(),
}
}
pub fn project(name: impl Into<String>) -> Self {
Self {
layer_type: DockerLayerType::Project,
name: name.into(),
}
}
}
impl fmt::Display for DockerLayer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", self.layer_type, self.name)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DockerImageConfig {
pub stack: String,
pub agent: String,
pub project: String,
}
impl DockerImageConfig {
pub fn new(stack: String, agent: String, project: String) -> Self {
Self {
stack,
agent,
project,
}
}
pub fn image_tag(&self) -> String {
format!("tsk/{}/{}/{}", self.stack, self.agent, self.project)
}
pub fn get_layers(&self) -> Vec<DockerLayer> {
vec![
DockerLayer::base(),
DockerLayer::stack(&self.stack),
DockerLayer::agent(&self.agent),
DockerLayer::project(&self.project),
]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_docker_layer_creation() {
let base = DockerLayer::base();
assert_eq!(base.layer_type, DockerLayerType::Base);
assert_eq!(base.name, "default");
let rust = DockerLayer::stack("rust");
assert_eq!(rust.layer_type, DockerLayerType::Stack);
assert_eq!(rust.name, "rust");
let claude = DockerLayer::agent("claude");
assert_eq!(claude.layer_type, DockerLayerType::Agent);
assert_eq!(claude.name, "claude");
let web_api = DockerLayer::project("web-api");
assert_eq!(web_api.layer_type, DockerLayerType::Project);
assert_eq!(web_api.name, "web-api");
}
#[test]
fn test_docker_image_config() {
let config = DockerImageConfig::new(
"rust".to_string(),
"claude".to_string(),
"web-api".to_string(),
);
assert_eq!(config.image_tag(), "tsk/rust/claude/web-api");
let layers = config.get_layers();
assert_eq!(layers.len(), 4);
assert_eq!(layers[0].layer_type, DockerLayerType::Base);
assert_eq!(layers[1].layer_type, DockerLayerType::Stack);
assert_eq!(layers[2].layer_type, DockerLayerType::Agent);
assert_eq!(layers[3].layer_type, DockerLayerType::Project);
}
#[test]
fn test_default_config() {
let config = DockerImageConfig {
stack: "default".to_string(),
agent: "claude".to_string(),
project: "default".to_string(),
};
assert_eq!(config.stack, "default");
assert_eq!(config.agent, "claude");
assert_eq!(config.project, "default");
assert_eq!(config.image_tag(), "tsk/default/claude/default");
}
}