use anyhow::{bail, Result};
use base64::Engine;
use compact_str::CompactString;
use netidx_derive::Pack;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::{fmt, str::FromStr};
#[derive(
Clone,
Copy,
Hash,
PartialEq,
Eq,
PartialOrd,
Ord,
Pack,
Serialize,
Deserialize,
JsonSchema,
)]
#[cfg_attr(feature = "juniper", derive(juniper::GraphQLScalar))]
#[pack(unwrapped)]
pub struct OrderId(u64);
impl OrderId {
pub fn new_unchecked(id: u64) -> Self {
Self(id)
}
pub fn to_u64(&self) -> u64 {
self.0
}
pub fn encode_base64(&self) -> Result<CompactString> {
let base64 = base64::engine::general_purpose::STANDARD;
let bytes = self.0.to_be_bytes();
let mut output_buf: [u8; 12] = [0; 12];
let size = base64.encode_slice(&bytes, &mut output_buf)?;
let cs = CompactString::from_utf8_lossy(&output_buf[0..size]);
Ok(cs)
}
pub fn decode_base64(input: impl AsRef<[u8]>) -> Result<Self> {
let bytes = base64::engine::general_purpose::STANDARD.decode(input)?;
if bytes.len() != 8 {
bail!("incorrect number of bytes to decode OrderId from base64");
}
let oid = u64::from_be_bytes(bytes.as_slice().try_into().unwrap()); Ok(Self(oid))
}
}
impl FromStr for OrderId {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(u64::from_str(s)?))
}
}
impl fmt::Debug for OrderId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl fmt::Display for OrderId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(feature = "rusqlite")]
impl rusqlite::ToSql for OrderId {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
use rusqlite::types::{ToSqlOutput, Value};
let val = Value::Integer(self.0 as i64);
Ok(ToSqlOutput::Owned(val))
}
}
#[cfg(feature = "juniper")]
impl OrderId {
fn to_output<S: juniper::ScalarValue>(&self) -> juniper::Value<S> {
juniper::Value::scalar(self.0.to_string())
}
fn from_input<S>(v: &juniper::InputValue<S>) -> Result<Self, String>
where
S: juniper::ScalarValue,
{
v.as_string_value()
.map(|s| u64::from_str(s))
.ok_or_else(|| format!("Expected `String`, found: {v}"))?
.map(|oid| Self(oid))
.map_err(|e| e.to_string())
}
fn parse_token<S>(value: juniper::ScalarToken<'_>) -> juniper::ParseScalarResult<S>
where
S: juniper::ScalarValue,
{
<String as juniper::ParseScalarValue<S>>::from_str(value)
}
}