use core::fmt;
use std::{ops::Deref, str::FromStr};
use crate::{hcstr::Str, hcstrid, packed_value, symbology::Symbolic};
use anyhow::{bail, Result};
use arrayvec::ArrayString;
use netidx::{
pack::Pack,
protocol::value::{FromValue, Value},
utils::pack,
};
use netidx_derive::Pack;
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
use serde::{Deserialize, Serialize};
pub mod alerts;
pub mod b2c2;
pub mod binance;
pub mod binance_futures;
pub mod book;
pub mod coinbase;
pub mod creds;
pub mod dropcopy;
pub mod dvchain;
pub mod limits;
pub mod oms_query;
pub mod orderflow;
pub mod otc_cpty;
pub mod rfq;
pub mod secrets;
pub mod symbology;
pub mod tms;
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Pack,
Serialize,
Deserialize,
JsonSchema,
)]
#[repr(i8)]
pub enum Dir {
Buy = 1,
Sell = -1,
}
packed_value!(Dir);
impl FromStr for Dir {
type Err = anyhow::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
"Buy" => Ok(Self::Buy),
"Sell" => Ok(Self::Sell),
s => bail!("{} is not a valid direction", s),
}
}
}
impl Dir {
pub fn flip(self) -> Self {
match self {
Self::Buy => Self::Sell,
Self::Sell => Self::Buy,
}
}
pub fn to_str_uppercase(self) -> &'static str {
match self {
Self::Buy => "BUY",
Self::Sell => "SELL",
}
}
pub fn from_str_uppercase(s: &str) -> Result<Self> {
match s {
"BUY" => Ok(Self::Buy),
"SELL" => Ok(Self::Sell),
_ => bail!("invalid format: {}", s),
}
}
pub fn to_str_lowercase(self) -> &'static str {
match self {
Self::Buy => "buy",
Self::Sell => "sell",
}
}
pub fn from_str_lowercase(s: &str) -> Result<Self> {
match s {
"buy" => Ok(Self::Buy),
"sell" => Ok(Self::Sell),
_ => bail!("invalid format: {}", s),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Pack, Serialize, Deserialize, JsonSchema)]
pub struct DirPair<T: 'static> {
pub buy: T,
pub sell: T,
}
impl<T: Into<Value> + Pack + 'static> Into<Value> for DirPair<T> {
fn into(self) -> Value {
Value::Bytes(pack(&self).unwrap().freeze())
}
}
impl<T: FromValue + Pack + 'static> FromValue for DirPair<T> {
fn from_value(v: Value) -> Result<Self> {
match v {
Value::Bytes(mut b) => Ok(Pack::decode(&mut b)?),
_ => bail!("invalid value, expected a bytes {:?}", v),
}
}
}
impl<T: Default + 'static> Default for DirPair<T> {
fn default() -> Self {
Self { buy: T::default(), sell: T::default() }
}
}
impl<T: 'static> DirPair<T> {
pub fn get(&self, dir: Dir) -> &T {
match dir {
Dir::Buy => &self.buy,
Dir::Sell => &self.sell,
}
}
pub fn get_mut(&mut self, dir: Dir) -> &mut T {
match dir {
Dir::Buy => &mut self.buy,
Dir::Sell => &mut self.sell,
}
}
}
impl DirPair<Decimal> {
pub fn is_empty(&self) -> bool {
self.buy == dec!(0) && self.sell == dec!(0)
}
pub fn net(&self) -> Decimal {
self.buy - self.sell
}
}
hcstrid!(Desk);
packed_value!(Desk);
hcstrid!(Trader);
packed_value!(Trader);
hcstrid!(Account);
packed_value!(Account);
#[derive(
Debug,
Clone,
Copy,
Hash,
PartialEq,
Eq,
PartialOrd,
Ord,
Pack,
Serialize,
Deserialize,
JsonSchema,
)]
#[pack(unwrapped)]
#[serde(transparent)]
pub struct FixedStr<const C: usize>(#[schemars(with = "String")] ArrayString<C>);
#[macro_export]
macro_rules! format_fixed {
($sz:expr, $f:expr, $($arg:expr),*) => {{
use std::fmt::Write;
let mut s = FixedStr::<$sz>::new();
write!(s, $f, $($arg),*).unwrap();
s
}};
}
impl<const C: usize> fmt::Write for FixedStr<C> {
fn write_str(&mut self, mut s: &str) -> fmt::Result {
let mut i = 1;
loop {
if s.len() <= C - self.len() {
break self.0.write_str(s);
} else if s.is_char_boundary(C - self.len() - i) {
s = s.split_at(C - self.len() - i).0
} else if i > 0 {
i -= 1;
} else {
break Ok(());
}
}
}
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
self.0.write_fmt(args)
}
}
impl<const C: usize> AsRef<str> for FixedStr<C> {
fn as_ref(&self) -> &str {
&**self
}
}
impl<const C: usize> Deref for FixedStr<C> {
type Target = str;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl<'a, const C: usize> TryFrom<&'a str> for FixedStr<C> {
type Error = arrayvec::CapacityError<&'a str>;
fn try_from(s: &'a str) -> std::result::Result<Self, Self::Error> {
Ok(Self(ArrayString::from(s)?))
}
}
impl<const C: usize> FromStr for FixedStr<C> {
type Err = arrayvec::CapacityError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match ArrayString::from(s) {
Ok(s) => Ok(Self(s)),
Err(_) => Err(arrayvec::CapacityError::new(())),
}
}
}
impl<const C: usize> FixedStr<C> {
pub fn new() -> Self {
Self(ArrayString::new())
}
pub fn from_lossy_end(mut s: &str) -> Self {
let mut i = 1;
loop {
if s.len() <= C {
break Self(ArrayString::from(s).unwrap());
} else if s.is_char_boundary(C - i) {
s = s.split_at(C - i).0
} else if i > 0 {
i -= 1;
} else {
break Self(ArrayString::new());
}
}
}
pub fn from_lossy_start(mut s: &str) -> Self {
let mut i = 0;
loop {
if s.len() <= C {
break Self(ArrayString::from(s).unwrap());
} else if s.is_char_boundary(s.len() - C - i) {
s = s.split_at(s.len() - C - i).1;
} else if i > 0 {
i -= 1;
} else {
break Self(ArrayString::new());
}
}
}
}