use crate::selector::{ProjectMismatch, Target, resolve_target};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum LogsPlan {
Show {
target: Target,
follow: bool,
},
Path {
target: Target,
all: bool,
},
Purge {
target: Target,
},
Prune {
max_size: Option<String>,
max_age: Option<String>,
},
Supervisor,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Modes {
pub path: bool,
pub purge: bool,
pub prune: bool,
pub follow: bool,
pub supervisor: bool,
}
#[derive(Debug, PartialEq, Eq)]
pub enum LogsPlanError {
ConflictingModes {
modes: Vec<&'static str>,
},
FollowWithMode {
mode: &'static str,
},
PruneBoundMissing,
SupervisorWithSelector,
TargetRequired,
Mismatch(ProjectMismatch),
}
pub fn resolve_plan(
modes: Modes,
service: Option<&str>,
project: Option<&str>,
max_size: Option<String>,
max_age: Option<String>,
) -> Result<LogsPlan, LogsPlanError> {
if modes.supervisor
&& (service.is_some()
|| project.is_some()
|| modes.path
|| modes.purge
|| modes.prune
|| modes.follow)
{
return Err(LogsPlanError::SupervisorWithSelector);
}
if modes.supervisor {
return Ok(LogsPlan::Supervisor);
}
let mut set = Vec::new();
if modes.path {
set.push("--path");
}
if modes.purge {
set.push("--purge");
}
if modes.prune {
set.push("--prune");
}
if set.len() > 1 {
return Err(LogsPlanError::ConflictingModes { modes: set });
}
if modes.follow
&& let Some(mode) = set.first()
{
return Err(LogsPlanError::FollowWithMode { mode });
}
if modes.prune {
if max_size.is_none() && max_age.is_none() {
return Err(LogsPlanError::PruneBoundMissing);
}
return Ok(LogsPlan::Prune { max_size, max_age });
}
let target = resolve_target(service, project).map_err(LogsPlanError::Mismatch)?;
if modes.path {
return Ok(LogsPlan::Path { target, all: false });
}
if modes.purge {
return Ok(LogsPlan::Purge { target });
}
if matches!(target, Target::Everything) {
return Err(LogsPlanError::TargetRequired);
}
Ok(LogsPlan::Show {
target,
follow: modes.follow,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn modes(path: bool, purge: bool, prune: bool, follow: bool) -> Modes {
Modes {
path,
purge,
prune,
follow,
supervisor: false,
}
}
#[test]
fn bare_logs_requires_a_target() {
assert_eq!(
resolve_plan(modes(false, false, false, false), None, None, None, None),
Err(LogsPlanError::TargetRequired)
);
}
#[test]
fn project_alone_is_a_show() {
assert_eq!(
resolve_plan(
modes(false, false, false, false),
None,
Some("alpha"),
None,
None
)
.unwrap(),
LogsPlan::Show {
target: Target::Project {
project: "alpha".into()
},
follow: false
}
);
}
#[test]
fn supervisor_alone_resolves() {
let modes = Modes {
supervisor: true,
..modes(false, false, false, false)
};
assert_eq!(
resolve_plan(modes, None, None, None, None).unwrap(),
LogsPlan::Supervisor
);
}
#[test]
fn supervisor_with_selector_is_rejected() {
let modes = Modes {
supervisor: true,
..modes(false, false, false, false)
};
assert_eq!(
resolve_plan(modes, Some("web"), None, None, None),
Err(LogsPlanError::SupervisorWithSelector)
);
}
#[test]
fn follow_is_a_following_show() {
assert_eq!(
resolve_plan(
modes(false, false, false, true),
Some("web"),
None,
None,
None
)
.unwrap(),
LogsPlan::Show {
target: Target::Service {
service: "web".into(),
project: None
},
follow: true
}
);
}
#[test]
fn two_mode_flags_conflict() {
assert!(matches!(
resolve_plan(modes(true, true, false, false), None, None, None, None),
Err(LogsPlanError::ConflictingModes { .. })
));
}
#[test]
fn follow_with_a_mode_is_rejected() {
assert!(matches!(
resolve_plan(modes(true, false, false, true), None, None, None, None),
Err(LogsPlanError::FollowWithMode { mode: "--path" })
));
}
#[test]
fn prune_without_bounds_is_rejected() {
assert_eq!(
resolve_plan(modes(false, false, true, false), None, None, None, None),
Err(LogsPlanError::PruneBoundMissing)
);
}
#[test]
fn prune_with_a_bound_resolves() {
assert_eq!(
resolve_plan(
modes(false, false, true, false),
None,
None,
Some("500MB".into()),
None
)
.unwrap(),
LogsPlan::Prune {
max_size: Some("500MB".into()),
max_age: None
}
);
}
#[test]
fn purge_resolves_with_selector() {
assert_eq!(
resolve_plan(
modes(false, true, false, false),
None,
Some("demo"),
None,
None
)
.unwrap(),
LogsPlan::Purge {
target: Target::Project {
project: "demo".into()
}
}
);
}
}