use serde_json::{Map, Value, json};
use crate::boundary::codec::fields::{list_of, opt, str_of, usize_of};
use crate::boundary::codec::spend::{rates_of, usd_of};
use crate::spend::{Ceiling, Cost, PriceRow, decimal};
pub(super) const KIND: &str = "prices";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PricesView {
pub rows: Vec<PriceRow>,
pub ceiling: Ceiling,
pub spent: Option<Cost>,
pub released: Option<usize>,
}
pub(super) fn reply(view: &PricesView) -> Value {
let mut map = Map::new();
map.insert("ok".to_owned(), json!(true));
map.insert("kind".to_owned(), json!(KIND));
map.insert(
"rows".to_owned(),
Value::Array(view.rows.iter().map(row).collect()),
);
if let Some(micro) = view.ceiling.micro_usd() {
map.insert("ceiling".to_owned(), decimal(micro));
}
super::cost::opt_cost("spent", view.spent.as_ref(), &mut map);
if let Some(released) = view.released {
map.insert("released".to_owned(), json!(released));
}
Value::Object(map)
}
fn row(row: &PriceRow) -> Value {
let mut map = Map::new();
map.insert("provider".to_owned(), json!(row.provider));
map.insert("model".to_owned(), json!(row.model));
map.append(&mut row.rates.json_fields());
Value::Object(map)
}
pub(super) fn view_of(o: &Map<String, Value>) -> Result<PricesView, String> {
Ok(PricesView {
rows: list_of(o, "rows", row_of)?,
ceiling: match o.get("ceiling") {
Some(usd) => {
usd_of(usd, "ceiling")?;
Ceiling::from_json(Some(usd))
}
None => Ceiling::default(),
},
spent: crate::boundary::codec::fields::opt_val(o, "spent", super::cost::cost_of)?,
released: opt(o, "released", usize_of)?,
})
}
fn row_of(v: &Value) -> Result<PriceRow, String> {
let o = v.as_object().ok_or("price row: not an object")?;
Ok(PriceRow {
provider: str_of(o, "provider")?,
model: str_of(o, "model")?,
rates: rates_of(v)?,
})
}