use std::str::FromStr;
use strum::{EnumCount, IntoEnumIterator};
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
strum::EnumCount,
strum::EnumIter,
strum::EnumProperty,
strum::EnumString,
strum::FromRepr,
strum::AsRefStr,
strum::IntoStaticStr,
)]
#[strum(serialize_all = "kebab-case")]
#[repr(i32)]
pub enum PackageStateKind {
#[strum(props(pbulk = "prefailed"))]
PreSkipped = 0,
#[strum(props(pbulk = "prefailed"))]
PreFailed = 1,
#[strum(props(pbulk = "prefailed"))]
Unresolved = 2,
#[strum(props(pbulk = "indirect-prefailed"))]
IndirectPreSkipped = 3,
#[strum(props(pbulk = "indirect-prefailed"))]
IndirectPreFailed = 4,
#[strum(props(pbulk = "indirect-prefailed"))]
IndirectUnresolved = 5,
#[strum(props(pbulk = "open"))]
Pending = 6,
#[strum(props(pbulk = "done"))]
UpToDate = 7,
#[strum(props(pbulk = "done"))]
Success = 8,
#[strum(props(pbulk = "failed"))]
Failed = 9,
#[strum(props(pbulk = "indirect-failed"))]
IndirectFailed = 10,
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum PackageState {
PreSkipped(String),
PreFailed(String),
Unresolved(String),
IndirectPreSkipped(String),
IndirectPreFailed(String),
IndirectUnresolved(String),
Pending,
UpToDate,
Success,
Failed(String),
IndirectFailed(String),
}
impl PackageState {
pub fn kind(&self) -> PackageStateKind {
match self {
Self::PreSkipped(_) => PackageStateKind::PreSkipped,
Self::PreFailed(_) => PackageStateKind::PreFailed,
Self::Unresolved(_) => PackageStateKind::Unresolved,
Self::IndirectPreSkipped(_) => PackageStateKind::IndirectPreSkipped,
Self::IndirectPreFailed(_) => PackageStateKind::IndirectPreFailed,
Self::IndirectUnresolved(_) => PackageStateKind::IndirectUnresolved,
Self::Pending => PackageStateKind::Pending,
Self::UpToDate => PackageStateKind::UpToDate,
Self::Success => PackageStateKind::Success,
Self::Failed(_) => PackageStateKind::Failed,
Self::IndirectFailed(_) => PackageStateKind::IndirectFailed,
}
}
fn from_kind(kind: PackageStateKind, detail: String) -> Self {
match kind {
PackageStateKind::PreSkipped => Self::PreSkipped(detail),
PackageStateKind::PreFailed => Self::PreFailed(detail),
PackageStateKind::Unresolved => Self::Unresolved(detail),
PackageStateKind::IndirectPreSkipped => Self::IndirectPreSkipped(detail),
PackageStateKind::IndirectPreFailed => Self::IndirectPreFailed(detail),
PackageStateKind::IndirectUnresolved => Self::IndirectUnresolved(detail),
PackageStateKind::Pending => Self::Pending,
PackageStateKind::UpToDate => Self::UpToDate,
PackageStateKind::Success => Self::Success,
PackageStateKind::Failed => Self::Failed(detail),
PackageStateKind::IndirectFailed => Self::IndirectFailed(detail),
}
}
pub fn status(&self) -> &'static str {
self.kind().into()
}
pub fn pbulk_status(&self) -> &'static str {
use strum::EnumProperty;
self.kind().get_str("pbulk").expect("pbulk prop")
}
pub fn db_id(&self) -> i32 {
self.kind() as i32
}
pub fn from_db(id: i32, detail: Option<String>) -> Option<Self> {
PackageStateKind::from_repr(id).map(|k| Self::from_kind(k, detail.unwrap_or_default()))
}
pub fn from_status(s: &str) -> Option<Self> {
PackageStateKind::from_str(s)
.ok()
.map(|k| Self::from_kind(k, String::new()))
}
pub fn detail(&self) -> Option<&str> {
match self {
Self::Pending | Self::UpToDate | Self::Success => None,
Self::PreSkipped(s)
| Self::PreFailed(s)
| Self::Unresolved(s)
| Self::IndirectPreSkipped(s)
| Self::IndirectPreFailed(s)
| Self::IndirectUnresolved(s)
| Self::Failed(s)
| Self::IndirectFailed(s) => Some(s),
}
}
pub fn is_skip(&self) -> bool {
!matches!(self, Self::Success | Self::Failed(_) | Self::UpToDate)
}
pub fn is_direct_skip(&self) -> bool {
matches!(
self,
Self::PreSkipped(_) | Self::PreFailed(_) | Self::Unresolved(_)
)
}
pub fn indirect(&self, detail: String) -> Self {
match self {
Self::PreSkipped(_) | Self::IndirectPreSkipped(_) => Self::IndirectPreSkipped(detail),
Self::PreFailed(_) | Self::IndirectPreFailed(_) => Self::IndirectPreFailed(detail),
Self::Unresolved(_) | Self::IndirectUnresolved(_) => Self::IndirectUnresolved(detail),
Self::IndirectFailed(_) => Self::IndirectFailed(detail),
other => other.clone(),
}
}
pub fn db_values() -> String {
PackageStateKind::iter()
.filter(|k| *k != PackageStateKind::Pending)
.map(|k| {
let s: &'static str = k.into();
format!("({}, '{}')", k as i32, s)
})
.collect::<Vec<_>>()
.join(", ")
}
}
#[derive(Clone, Debug)]
pub struct PackageCounts([usize; PackageStateKind::COUNT]);
impl Default for PackageCounts {
fn default() -> Self {
Self([0; PackageStateKind::COUNT])
}
}
impl PackageCounts {
pub fn add(&mut self, state: &PackageState) {
self.0[state.kind() as usize] += 1;
}
pub fn succeeded(&self) -> usize {
self[PackageStateKind::Success]
}
pub fn failed(&self) -> usize {
self[PackageStateKind::Failed]
}
pub fn up_to_date(&self) -> usize {
self[PackageStateKind::UpToDate]
}
pub fn masked(&self) -> usize {
use PackageStateKind::*;
self[PreSkipped]
+ self[PreFailed]
+ self[IndirectPreSkipped]
+ self[IndirectPreFailed]
+ self[IndirectUnresolved]
+ self[IndirectFailed]
}
pub fn total(&self) -> usize {
self.succeeded() + self.failed() + self.up_to_date() + self.masked()
}
}
impl std::ops::Index<PackageStateKind> for PackageCounts {
type Output = usize;
fn index(&self, kind: PackageStateKind) -> &usize {
&self.0[kind as usize]
}
}
impl std::fmt::Display for PackageState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.detail() {
Some(d) if !d.is_empty() => write!(f, "{}", d),
_ => write!(f, "{}", self.status()),
}
}
}