use serde::{Deserialize, Serialize};
use crate::status::Health;
pub const PROJECT_LABEL: &str = "com.docker.compose.project";
pub const SERVICE_LABEL: &str = "com.docker.compose.service";
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum EventAction {
Create,
Start,
Stop,
Kill,
Die {
exit_code: Option<i32>,
},
Destroy,
Health(Health),
Other(String),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct EngineEvent {
pub project: String,
pub service: String,
pub container: String,
pub action: EventAction,
pub time_nanos: i64,
}
pub fn parse_event(line: &str) -> Result<Option<EngineEvent>, serde_json::Error> {
#[derive(Deserialize)]
struct Actor {
#[serde(rename = "Attributes", default)]
attributes: std::collections::BTreeMap<String, String>,
}
#[derive(Deserialize)]
struct Raw {
#[serde(rename = "Type", default)]
kind: String,
#[serde(rename = "Action", default)]
action: String,
#[serde(rename = "Actor")]
actor: Actor,
#[serde(rename = "timeNano", default)]
time_nano: i64,
}
let raw: Raw = serde_json::from_str(line)?;
if raw.kind != "container" {
return Ok(None);
}
let attrs = &raw.actor.attributes;
let (Some(project), Some(service)) = (attrs.get(PROJECT_LABEL), attrs.get(SERVICE_LABEL))
else {
return Ok(None);
};
let action = match raw.action.split_once(':') {
Some(("health_status", health)) => EventAction::Health(Health::parse(health)),
Some((name, _)) if name.starts_with("exec_") => return Ok(None),
_ => match raw.action.as_str() {
"create" => EventAction::Create,
"start" => EventAction::Start,
"stop" => EventAction::Stop,
"kill" => EventAction::Kill,
"die" => EventAction::Die {
exit_code: attrs.get("exitCode").and_then(|c| c.parse().ok()),
},
"destroy" => EventAction::Destroy,
other if other.starts_with("exec_") => return Ok(None),
other => EventAction::Other(other.to_owned()),
},
};
Ok(Some(EngineEvent {
project: project.clone(),
service: service.clone(),
container: attrs.get("name").cloned().unwrap_or_default(),
action,
time_nanos: raw.time_nano,
}))
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum EventFilter {
Project(String),
Prefix(String),
All,
}
impl EventFilter {
pub fn matches(&self, event: &EngineEvent) -> bool {
match self {
EventFilter::Project(name) => event.project == *name,
EventFilter::Prefix(prefix) => event.project.starts_with(prefix.as_str()),
EventFilter::All => true,
}
}
}