Skip to main content

hyperi_rustlib/deployment/generate/
compose.rs

1// Project:   hyperi-rustlib
2// File:      src/deployment/generate/compose.rs
3// Purpose:   Docker Compose fragment generation
4// Language:  Rust
5//
6// License:   BUSL-1.1
7// Copyright: (c) 2026 HYPERI PTY LIMITED
8
9#![allow(clippy::format_push_string)]
10
11use crate::deployment::contract::DeploymentContract;
12
13// ============================================================================
14// Docker Compose fragment
15// ============================================================================
16
17/// Generate a Docker Compose service fragment from the deployment contract.
18#[must_use]
19pub fn generate_compose_fragment(contract: &DeploymentContract) -> String {
20    let binary = contract.binary();
21    let mut out = String::with_capacity(512);
22
23    // Service definition
24    out.push_str(&format!(
25        "# Generated by hyperi-rustlib deployment module\nservices:\n  {}:\n",
26        contract.app_name
27    ));
28
29    // Image
30    out.push_str(&format!(
31        "    image: {}/{}:${{{}_VERSION:-latest}}\n",
32        contract.image_registry,
33        contract.app_name,
34        contract.env_prefix.replace("__", "_")
35    ));
36
37    // depends_on
38    if !contract.depends_on.is_empty() {
39        out.push_str("    depends_on:\n");
40        for dep in &contract.depends_on {
41            out.push_str(&format!(
42                "      {dep}:\n        condition: service_healthy\n"
43            ));
44        }
45    }
46
47    // Ports
48    out.push_str("    ports:\n");
49    out.push_str(&format!(
50        "      - \"{}:{}\"\n",
51        contract.metrics_port, contract.metrics_port
52    ));
53    for p in &contract.extra_ports {
54        out.push_str(&format!("      - \"{}:{}\"\n", p.port, p.port));
55    }
56
57    // Volumes -- config file mount
58    out.push_str("    volumes:\n");
59    out.push_str(&format!(
60        "      - ./config/{}:{}:ro\n",
61        contract.config_filename(),
62        contract.config_mount_path,
63    ));
64
65    // Healthcheck
66    out.push_str(&format!(
67        "    healthcheck:\n\
68         \x20     test: [\"CMD\", \"curl\", \"-sf\", \"http://localhost:{}{}\"]
69      interval: 10s\n\
70         \x20     timeout: 3s\n\
71         \x20     retries: 5\n",
72        contract.metrics_port, contract.health.liveness_path,
73    ));
74
75    // Entrypoint args
76    if !contract.entrypoint_args.is_empty() {
77        out.push_str(&format!("    command: [\"{binary}\""));
78        for arg in &contract.entrypoint_args {
79            out.push_str(&format!(", \"{arg}\""));
80        }
81        out.push_str("]\n");
82    }
83
84    out
85}