use crate::wip::{Estimate as WipEstimate, WipConfig, WipDiagnostic};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct EstimateValue {
pub raw: Option<String>,
pub unit: Option<String>,
pub value: Option<u32>,
pub converted_value: Option<u32>,
pub diagnostics: Vec<WipDiagnostic>,
converted_in: Option<String>,
}
impl EstimateValue {
#[must_use]
pub fn unestimated() -> Self {
Self::default()
}
#[must_use]
pub fn parse(number: u32, raw: &str) -> Self {
match WipEstimate::parse(raw.trim()) {
Ok(estimate) => Self {
raw: Some(raw.to_string()),
unit: estimate.unit,
value: Some(estimate.value),
converted_value: None,
diagnostics: Vec::new(),
converted_in: None,
},
Err(error) => Self {
raw: Some(raw.to_string()),
unit: None,
value: None,
converted_value: None,
diagnostics: error
.diagnostics()
.iter()
.map(|finding| {
located(
number,
format!("states `{raw}`, which cannot be counted"),
finding,
)
})
.collect(),
converted_in: None,
},
}
}
#[must_use]
pub fn converted_to(&self, number: u32, registry: &WipConfig, unit: &str) -> Self {
let mut value = self.clone();
let (Some(raw), Some(amount)) = (self.raw.as_deref(), self.value) else {
return value;
};
let estimate = WipEstimate {
value: amount,
unit: self.unit.clone(),
};
match registry.convert(&estimate, unit) {
Ok(converted) => {
value.converted_value = Some(converted);
value.converted_in = Some(unit.to_string());
}
Err(error) => value
.diagnostics
.extend(error.diagnostics().iter().map(|finding| {
located(
number,
format!("states `{raw}`, which cannot be counted in `{unit}`"),
finding,
)
})),
}
value
}
#[must_use]
pub fn raw(&self) -> Option<&str> {
self.raw.as_deref()
}
#[must_use]
pub fn unit(&self) -> Option<&str> {
self.unit.as_deref()
}
#[must_use]
pub fn converted_in(&self) -> Option<&str> {
self.converted_in.as_deref()
}
#[must_use]
pub fn diagnostics(&self) -> &[WipDiagnostic] {
&self.diagnostics
}
#[must_use]
pub fn is_unestimated(&self) -> bool {
self.raw.is_none()
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.value.is_some()
}
#[must_use]
pub fn is_converted(&self) -> bool {
self.converted_value.is_some()
}
#[must_use]
pub fn render(&self) -> String {
self.raw
.clone()
.unwrap_or_else(|| "unestimated".to_string())
}
}
fn located(number: u32, context: String, finding: &WipDiagnostic) -> WipDiagnostic {
WipDiagnostic {
kind: finding.kind,
message: format!("issue #{number} {context}: {}", finding.message),
because: finding.because.clone(),
}
}