1use super::demand;
2use crate::models::{AuthId, BidderId, DemandCurve, map_wrapper, uuid_wrapper};
3use serde::{Deserialize, Serialize};
4use std::hash::Hash;
5use time::OffsetDateTime;
6use utoipa::ToSchema;
7
8uuid_wrapper!(CostId);
10
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
14#[serde(try_from = "RawCost", into = "RawCost")]
15pub struct CostData {
16 pub demand: DemandCurve,
18}
19
20impl CostData {
21 pub fn new(demand: DemandCurve) -> Self {
23 Self { demand }
24 }
25}
26
27#[derive(Serialize, Deserialize)]
29pub struct RawCost {
30 pub demand: demand::RawDemandCurve,
31}
32
33impl TryFrom<RawCost> for CostData {
34 type Error = demand::ValidationError;
35
36 fn try_from(value: RawCost) -> Result<Self, Self::Error> {
37 Ok(CostData::new(value.demand.try_into()?))
38 }
39}
40
41impl From<CostData> for RawCost {
42 fn from(value: CostData) -> Self {
43 Self {
44 demand: value.demand.into(),
45 }
46 }
47}
48
49#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
54#[serde(rename_all = "lowercase")]
55pub enum GroupDisplay {
56 Exclude,
58 Include,
60}
61
62impl Default for GroupDisplay {
63 fn default() -> Self {
64 Self::Exclude
65 }
66}
67
68#[derive(Serialize, Deserialize, PartialEq, ToSchema, Debug)]
73pub struct CostHistoryRecord {
74 pub data: Option<CostData>,
76 #[serde(with = "time::serde::rfc3339")]
78 pub version: OffsetDateTime,
79}
80
81#[derive(Serialize, Deserialize, PartialEq, Debug, ToSchema)]
88pub struct CostRecord {
89 pub bidder_id: BidderId,
91
92 pub cost_id: CostId,
94
95 #[serde(default, skip_serializing_if = "Option::is_none")]
97 #[schema(value_type = Option<std::collections::HashMap<AuthId, f64>>)]
98 pub group: Option<Group>,
99
100 pub data: Option<CostData>,
102
103 #[serde(with = "time::serde::rfc3339")]
105 pub version: OffsetDateTime,
106}
107
108map_wrapper!(Group, AuthId, f64);
109
110impl CostRecord {
111 pub fn into_solver(
116 self,
117 scale: f64,
118 ) -> Option<fts_solver::DemandCurve<AuthId, Group, Vec<fts_solver::Point>>> {
119 if let Some(data) = self.data {
120 let group = self.group.unwrap_or_default();
121 let points = match data.demand {
122 DemandCurve::Curve(curve) => curve.as_solver(scale),
123 DemandCurve::Constant(constant) => constant.as_solver(scale),
124 };
125 let domain = (
126 points.first().map(|pt| pt.quantity).unwrap_or_default(),
127 points.last().map(|pt| pt.quantity).unwrap_or_default(),
128 );
129
130 Some(fts_solver::DemandCurve {
131 domain,
132 group,
133 points,
134 })
135 } else {
136 None
137 }
138 }
139}