use std::{fmt, iter::once, str::FromStr};
use miette::Diagnostic;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::{
int::{self, ParseError},
macros::errors,
};
pub const DISABLED: u64 = 0;
pub const DEFAULT: u64 = 1;
#[derive(Debug, Error, Diagnostic)]
#[error("failed to parse `{string}` to skew")]
#[diagnostic(code(otp_std::skew), 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 Skew {
value: u64,
}
errors! {
Type = Self::Err,
Hack = $,
error => new(error, string => to_owned),
}
impl FromStr for Skew {
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 Skew {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.get().fmt(formatter)
}
}
impl From<u64> for Skew {
fn from(value: u64) -> Self {
Self::new(value)
}
}
impl From<Skew> for u64 {
fn from(skew: Skew) -> Self {
skew.get()
}
}
impl Default for Skew {
fn default() -> Self {
Self::DEFAULT
}
}
impl Skew {
pub const fn new(value: u64) -> Self {
Self { value }
}
pub const fn get(self) -> u64 {
self.value
}
pub const fn disabled() -> Self {
Self::DISABLED
}
pub fn apply(self, value: u64) -> impl Iterator<Item = u64> {
let sub = (1..=self.get()).filter_map(move |offset| value.checked_sub(offset));
let add = (1..=self.get()).filter_map(move |offset| value.checked_add(offset));
sub.rev().chain(once(value)).chain(add)
}
pub const DISABLED: Self = Self::new(DISABLED);
pub const DEFAULT: Self = Self::new(DEFAULT);
}