use crate::str::Str;
use arrayvec::ArrayVec;
use core::error::Error;
use core::{fmt, slice};
mod ascii;
mod iso;
mod jis;
mod mac;
mod utf;
mod win;
pub use ascii::*;
pub use iso::*;
pub use jis::*;
pub use mac::*;
pub use utf::*;
pub use win::*;
mod sealed {
pub trait Sealed: Sized {}
}
use sealed::Sealed;
#[doc(hidden)]
pub trait ArrayLike {
fn slice(&self) -> &[u8];
}
impl<const N: usize> ArrayLike for ArrayVec<u8, N> {
fn slice(&self) -> &[u8] {
self
}
}
impl<const N: usize> ArrayLike for [u8; N] {
fn slice(&self) -> &[u8] {
self
}
}
impl ArrayLike for u8 {
fn slice(&self) -> &[u8] {
slice::from_ref(self)
}
}
pub trait Encoding: Default + Sealed {
#[doc(hidden)]
const REPLACEMENT: char;
#[doc(hidden)]
const MAX_LEN: usize;
#[doc(hidden)]
type Bytes: ArrayLike;
#[doc(hidden)]
fn shorthand() -> &'static str;
fn validate(bytes: &[u8]) -> Result<(), ValidateError>;
fn encode(char: char, out: &mut [u8]) -> Result<usize, EncodeError> {
match Self::encode_char(char) {
Some(a) => {
let a = a.slice();
if a.len() > out.len() {
Err(EncodeError::NeedSpace { len: a.len() })
} else {
out[..a.len()].copy_from_slice(a);
Ok(a.len())
}
}
None => Err(EncodeError::InvalidChar),
}
}
fn recode<E: Encoding>(str: &Str<E>, out: &mut [u8]) -> Result<usize, RecodeError> {
str.char_indices().try_fold(0, |out_pos, (idx, c)| {
match Self::encode(c, &mut out[out_pos..]) {
Ok(len) => Ok(out_pos + len),
Err(e) => Err(RecodeError {
input_used: idx,
output_valid: out_pos,
cause: match e {
EncodeError::NeedSpace { len } => RecodeCause::NeedSpace { len },
EncodeError::InvalidChar => RecodeCause::InvalidChar {
char: c,
len: E::char_len(c),
},
},
}),
}
})
}
#[doc(hidden)]
fn encode_char(c: char) -> Option<Self::Bytes>;
#[doc(hidden)]
fn decode_char(str: &Str<Self>) -> (char, &Str<Self>);
#[doc(hidden)]
fn char_bound(str: &Str<Self>, idx: usize) -> bool;
#[doc(hidden)]
fn char_len(c: char) -> usize;
}
pub trait NullTerminable: Encoding {}
pub trait AlwaysValid: Encoding {}
#[derive(Clone, Debug, PartialEq)]
pub struct ValidateError {
valid_up_to: usize,
error_len: Option<u8>,
}
impl ValidateError {
pub fn valid_up_to(&self) -> usize {
self.valid_up_to
}
pub fn error_len(&self) -> Option<usize> {
self.error_len.map(|e| e as usize)
}
}
impl fmt::Display for ValidateError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error while validating data")
}
}
impl Error for ValidateError {}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum EncodeError {
NeedSpace {
len: usize,
},
InvalidChar,
}
impl fmt::Display for EncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error while encoding data: ")?;
match self {
EncodeError::NeedSpace { len } => {
write!(f, "insufficient space, need {len} bytes for next character")
}
EncodeError::InvalidChar => write!(f, "invalid character for output encoding"),
}
}
}
impl Error for EncodeError {}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum RecodeCause {
NeedSpace {
len: usize,
},
InvalidChar {
char: char,
len: usize,
},
}
impl RecodeCause {
pub(crate) fn write_cause(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RecodeCause::NeedSpace { .. } => {
write!(f, "insufficient space")
}
RecodeCause::InvalidChar { char, .. } => {
write!(f, "invalid character for output encoding '{char}'")
}
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct RecodeError {
input_used: usize,
output_valid: usize,
cause: RecodeCause,
}
impl RecodeError {
pub fn input_used(&self) -> usize {
self.input_used
}
pub fn output_valid(&self) -> usize {
self.output_valid
}
pub fn cause(&self) -> &RecodeCause {
&self.cause
}
}
impl fmt::Display for RecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error while recoding data: ")?;
self.cause.write_cause(f)
}
}
impl Error for RecodeError {}