use chrono::{NaiveDate, TimeZone, Utc};
use orbital_data::DataValue;
use crate::types::ColumnType;
pub fn format_edit_value(value: &DataValue) -> String {
match value {
DataValue::Text(s) | DataValue::Category(s) => s.clone(),
DataValue::Number(n) => n.to_string(),
DataValue::Bool(b) => b.to_string(),
DataValue::Date(d) => d.format("%Y-%m-%d").to_string(),
DataValue::Null => String::new(),
}
}
pub fn date_value_to_unix(value: &DataValue) -> Option<i64> {
match value {
DataValue::Date(d) => d
.and_hms_opt(0, 0, 0)
.map(|dt| Utc.from_utc_datetime(&dt).timestamp()),
_ => None,
}
}
pub fn unix_to_date_text(unix: i64) -> String {
Utc.timestamp_opt(unix, 0)
.single()
.map(|dt| dt.date_naive().format("%Y-%m-%d").to_string())
.unwrap_or_default()
}
pub fn parse_edit_value(text: &str, col_type: ColumnType) -> Result<DataValue, String> {
let trimmed = text.trim();
match col_type {
ColumnType::Number => trimmed
.parse::<f64>()
.map(DataValue::Number)
.map_err(|_| "Enter a valid number".to_string()),
ColumnType::Boolean => match trimmed.to_ascii_lowercase().as_str() {
"true" | "1" | "yes" => Ok(DataValue::Bool(true)),
"false" | "0" | "no" => Ok(DataValue::Bool(false)),
"" => Ok(DataValue::Bool(false)),
_ => Err("Enter true or false".to_string()),
},
ColumnType::Date => {
if trimmed.is_empty() {
return Ok(DataValue::Null);
}
NaiveDate::parse_from_str(trimmed, "%Y-%m-%d")
.map(DataValue::Date)
.map_err(|_| "Enter a date as YYYY-MM-DD".to_string())
}
ColumnType::SingleSelect | ColumnType::Text | ColumnType::Actions => {
Ok(DataValue::Text(trimmed.to_string()))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_number() {
assert_eq!(
parse_edit_value("42.5", ColumnType::Number).unwrap(),
DataValue::Number(42.5)
);
}
#[test]
fn parse_bool() {
assert_eq!(
parse_edit_value("yes", ColumnType::Boolean).unwrap(),
DataValue::Bool(true)
);
}
}