Skip to main content

olai_stack_runtime/
reconcile.rs

1//! Matching what Docker runs against what the caller manages.
2
3use std::collections::BTreeSet;
4
5use crate::engine::ComposeEngine;
6use crate::error::Result;
7use crate::status::ProjectStatus;
8
9/// Which projects a caller manages.
10///
11/// Prefer [`Exact`](Self::Exact) when the caller knows its project names: a
12/// [`Prefix`](Self::Prefix) is only unambiguous when the prefix ends in a separator that the
13/// rest of a name can't contain. Workspace projects are `<workspace>-<env>` and both parts may
14/// contain dashes, so `demo-` also matches the projects of a workspace called `demo-x`.
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub enum ProjectMatch {
17    /// These names.
18    Exact(BTreeSet<String>),
19    /// Every name starting with this prefix.
20    Prefix(String),
21}
22
23impl ProjectMatch {
24    /// Whether `name` is one of the caller's.
25    pub fn matches(&self, name: &str) -> bool {
26        match self {
27            ProjectMatch::Exact(names) => names.contains(name),
28            ProjectMatch::Prefix(prefix) => name.starts_with(prefix.as_str()),
29        }
30    }
31}
32
33/// The projects Docker knows of (running or stopped) that `which` picks.
34pub async fn managed_projects(
35    engine: &dyn ComposeEngine,
36    which: &ProjectMatch,
37) -> Result<Vec<ProjectStatus>> {
38    Ok(engine
39        .ls()
40        .await?
41        .into_iter()
42        .filter(|p| which.matches(&p.name))
43        .collect())
44}
45
46/// Remove the shared `network` once none of `members` is known to Docker any more; returns
47/// whether it was removed.
48///
49/// Compose labels a network with the project that created it, so no member's `down` removes
50/// a network others joined: whoever stops the last member calls this.
51pub async fn remove_mesh_if_idle(
52    engine: &dyn ComposeEngine,
53    network: &str,
54    members: &BTreeSet<String>,
55) -> Result<bool> {
56    let running = managed_projects(engine, &ProjectMatch::Exact(members.clone())).await?;
57    if !running.is_empty() {
58        return Ok(false);
59    }
60    engine.network_rm(network).await
61}