use std::{fmt, str::FromStr};
use const_macros::const_none;
use miette::Diagnostic;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{
int::{self, ParseError},
macros::errors,
};
pub const DEFAULT: u64 = 0;
#[derive(Debug, Error, Diagnostic)]
#[error("failed to parse `{string}` to counter")]
#[diagnostic(code(otp_std::counter), help("see the report for more information"))]
pub struct Error {
#[source]
#[diagnostic_source]
pub source: ParseError,
pub string: String,
}
impl Error {
pub const fn new(source: ParseError, string: String) -> Self {
Self { source, string }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(from = "u64", into = "u64"))]
pub struct Counter {
value: u64,
}
errors! {
Type = Error,
Hack = $,
error => new(error, string => to_owned),
}
impl FromStr for Counter {
type Err = Error;
fn from_str(string: &str) -> Result<Self, Self::Err> {
let value = string
.parse()
.map_err(|error| error!(int::wrap(error), string))?;
Ok(Self::new(value))
}
}
impl fmt::Display for Counter {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.get().fmt(formatter)
}
}
impl From<u64> for Counter {
fn from(value: u64) -> Self {
Self::new(value)
}
}
impl From<Counter> for u64 {
fn from(counter: Counter) -> Self {
counter.get()
}
}
impl Default for Counter {
fn default() -> Self {
Self::DEFAULT
}
}
pub const OVERFLOW: &str = "overflow";
impl Counter {
pub const fn new(value: u64) -> Self {
Self { value }
}
pub const fn get(self) -> u64 {
self.value
}
#[must_use = "this method returns the incremented counter instead of modifying the original"]
pub const fn try_next(self) -> Option<Self> {
let value = const_none!(self.get().checked_add(1));
Some(Self::new(value))
}
#[must_use = "this method returns the incremented counter instead of modifying the original"]
pub const fn next(self) -> Self {
self.try_next().expect(OVERFLOW)
}
pub const DEFAULT: Self = Self::new(DEFAULT);
}