use core::fmt;
use core::str::FromStr;
use super::{Decode, DecodeError, Encode, Reader, Sink, ValueError, Writer, hex};
use crate::log::hash::{TAG_ENTRY_ID, tagged};
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LedgerId(String);
impl LedgerId {
pub const MAX_BYTES: usize = 255;
pub fn new(text: impl Into<String>) -> Result<Self, ValueError> {
let text = text.into();
if text.is_empty() {
return Err(ValueError::Empty);
}
if text.len() > Self::MAX_BYTES {
return Err(ValueError::TooLong {
max: Self::MAX_BYTES,
found: text.len(),
});
}
if let Some(found) = text.chars().find(|c| !matches!(c, '!'..='~') || *c == '+') {
return Err(ValueError::InvalidCharacter { found });
}
Ok(Self(text))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IdempotencyKey(String);
impl IdempotencyKey {
pub const MAX_BYTES: usize = 128;
pub fn new(text: impl Into<String>) -> Result<Self, ValueError> {
let text = text.into();
if text.is_empty() {
return Err(ValueError::Empty);
}
if text.len() > Self::MAX_BYTES {
return Err(ValueError::TooLong {
max: Self::MAX_BYTES,
found: text.len(),
});
}
if text.chars().any(char::is_control) {
return Err(ValueError::ControlCharacter);
}
Ok(Self(text))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
macro_rules! text_id {
($($ty:ident),*) => {$(
impl fmt::Display for $ty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
super::encoding::pad(f, &self.0)
}
}
impl fmt::Debug for $ty {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($ty), "({:?})"), self.0)
}
}
impl FromStr for $ty {
type Err = ValueError;
fn from_str(text: &str) -> Result<Self, Self::Err> {
Self::new(text)
}
}
impl TryFrom<&str> for $ty {
type Error = ValueError;
fn try_from(text: &str) -> Result<Self, Self::Error> {
Self::new(text)
}
}
impl TryFrom<String> for $ty {
type Error = ValueError;
fn try_from(text: String) -> Result<Self, Self::Error> {
Self::new(text)
}
}
impl AsRef<str> for $ty {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Encode for $ty {
fn encode<S: Sink>(&self, w: &mut Writer<S>) {
w.str(&self.0);
}
}
impl Decode for $ty {
fn decode(r: &mut Reader<'_>, field: &'static str) -> Result<Self, DecodeError> {
Self::new(r.str(field)?).map_err(|error| DecodeError::Value { field, error })
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for $ty {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for $ty {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
super::serde_str::deserialize(deserializer)
}
}
)*};
}
text_id!(LedgerId, IdempotencyKey);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EntryId([u8; 16]);
impl EntryId {
#[must_use]
pub fn derive(ledger: &LedgerId, key: &IdempotencyKey) -> Self {
let mut payload = Writer::new(Vec::new());
payload.value(ledger);
payload.value(key);
let digest = tagged(TAG_ENTRY_ID, &payload.into_inner());
let mut id = [0u8; 16];
for (slot, byte) in id.iter_mut().zip(digest.as_bytes()) {
*slot = *byte;
}
Self(id)
}
#[must_use]
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
}
impl fmt::Display for EntryId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
hex::write(f, &self.0)
}
}
impl fmt::Debug for EntryId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EntryId({self})")
}
}
impl FromStr for EntryId {
type Err = ValueError;
fn from_str(text: &str) -> Result<Self, Self::Err> {
hex::parse(text).map(Self)
}
}
impl Encode for EntryId {
fn encode<S: Sink>(&self, w: &mut Writer<S>) {
w.fixed(&self.0);
}
}
impl Decode for EntryId {
fn decode(r: &mut Reader<'_>, _field: &'static str) -> Result<Self, DecodeError> {
r.array().map(Self)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for EntryId {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(self)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for EntryId {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
super::serde_str::deserialize(deserializer)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct AccountHandle(u32);
impl AccountHandle {
pub const MAX: u32 = u32::MAX - 1;
#[must_use]
pub const fn new(index: u32) -> Option<Self> {
if index > Self::MAX {
None
} else {
Some(Self(index))
}
}
#[must_use]
pub const fn get(self) -> u32 {
self.0
}
}
impl fmt::Display for AccountHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
super::encoding::pad(f, &format!("#{}", self.0))
}
}
impl Encode for AccountHandle {
fn encode<S: Sink>(&self, w: &mut Writer<S>) {
w.u32(self.0);
}
}
impl Decode for AccountHandle {
fn decode(r: &mut Reader<'_>, field: &'static str) -> Result<Self, DecodeError> {
Self::new(r.u32()?).ok_or(DecodeError::Value {
field,
error: ValueError::OutOfRange,
})
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for AccountHandle {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de::Error as _;
Self::new(u32::deserialize(deserializer)?)
.ok_or_else(|| D::Error::custom(ValueError::OutOfRange))
}
}