use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::types::wire::wire_enum;
wire_enum! {
pub enum TickType {
Trade => "trade",
Quote => "quote",
}
}
wire_enum! {
pub enum Tape {
A => "A",
B => "B",
C => "C",
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Codes(HashMap<String, String>);
impl Codes {
#[must_use]
pub fn name(&self, code: &str) -> Option<&str> {
self.0.get(code).map(String::as_str)
}
pub fn names<'a>(&'a self, codes: &'a [String]) -> impl Iterator<Item = Option<&'a str>> {
codes.iter().map(|code| self.name(code))
}
#[must_use]
pub fn as_map(&self) -> &HashMap<String, String> {
&self.0
}
#[must_use]
pub fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl From<HashMap<String, String>> for Codes {
fn from(map: HashMap<String, String>) -> Self {
Self(map)
}
}
impl<'a> IntoIterator for &'a Codes {
type Item = (&'a String, &'a String);
type IntoIter = std::collections::hash_map::Iter<'a, String, String>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub(crate) struct TapeQuery {
pub tape: Tape,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_single_space_survives_the_lookup() {
let codes: Codes = serde_json::from_str(r#"{" ": "Regular Sale"}"#).unwrap();
assert_eq!(codes.name(" "), Some("Regular Sale"));
assert_eq!(codes.name(""), None);
assert_eq!(codes.name(" "), None);
}
#[test]
fn unknown_codes_read_as_absent_rather_than_erroring() {
let codes: Codes = serde_json::from_str(r#"{"I": "Odd Lot Trade"}"#).unwrap();
let conditions = vec![" ".to_owned(), "I".to_owned()];
let names: Vec<_> = codes.names(&conditions).collect();
assert_eq!(names, vec![None, Some("Odd Lot Trade")]);
}
#[test]
fn tape_is_required_by_the_query_type() {
let query = TapeQuery { tape: Tape::A };
assert_eq!(serde_json::to_value(&query).unwrap()["tape"], "A");
}
}