use chrono::NaiveDate;
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use crate::types::wire::wire_enum;
wire_enum! {
pub enum IpoAvailability {
Available => "available",
NotAvailable => "not_available",
Closed => "closed",
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct IpoOffering {
pub ipo_reference: String,
pub name: String,
pub offering_type: String,
pub availability: IpoAvailability,
pub no_new_orders: bool,
#[serde(with = "crate::types::decimal")]
pub min_price: Decimal,
#[serde(with = "crate::types::decimal")]
pub max_price: Decimal,
#[serde(default)]
pub ticker_symbol: Option<String>,
#[serde(default)]
pub cusip_id: Option<String>,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub anticipated_shares: Option<i64>,
#[serde(default)]
pub min_ticket_size: Option<String>,
#[serde(default)]
pub max_ticket_size: Option<String>,
#[serde(default)]
pub unit_step_size: Option<String>,
#[serde(default)]
pub trade_date: Option<NaiveDate>,
#[serde(default)]
pub settlement_date: Option<NaiveDate>,
#[serde(default)]
pub prospectus_url: Option<String>,
#[serde(default)]
pub logo_small: Option<String>,
#[serde(
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub underwriters: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct IpoOfferingsPage {
#[serde(
rename = "data",
default,
deserialize_with = "crate::types::serde_util::null_as_default"
)]
pub offerings: Vec<IpoOffering>,
#[serde(default)]
pub next_page_token: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct IpoOfferingResponse {
#[serde(rename = "data")]
pub offering: IpoOffering,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct GetIpoOfferingsRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub availability: Option<IpoAvailability>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ticker: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
}
impl GetIpoOfferingsRequest {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn availability(mut self, availability: IpoAvailability) -> Self {
self.availability = Some(availability);
self
}
#[must_use]
pub fn ticker(mut self, ticker: impl Into<String>) -> Self {
self.ticker = Some(ticker.into());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_list_and_the_single_offering_share_one_envelope() {
let page: IpoOfferingsPage = serde_json::from_value(serde_json::json!({
"data": [{
"ipo_reference": "IPO123",
"name": "Example Corp",
"offering_type": "ipo",
"availability": "available",
"no_new_orders": false,
"min_price": "17.00",
"max_price": "19.00",
}],
"next_page_token": null,
}))
.unwrap();
assert_eq!(page.offerings.len(), 1);
assert_eq!(page.offerings[0].min_price, Decimal::new(1700, 2));
assert!(!page.offerings[0].no_new_orders);
assert!(page.offerings[0].underwriters.is_empty());
}
}