use std::borrow::Cow;
use serde::{Deserialize, Serialize};
use crate::bridge::PositionEncoding;
#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
pub enum InvalidPositionEncodings {
#[error("position_encodings cannot be empty")]
Empty,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(try_from = "Vec<PositionEncoding>", into = "Vec<PositionEncoding>")]
pub struct PositionEncodings(Cow<'static, [PositionEncoding]>);
impl PositionEncodings {
pub const DEFAULT: Self = Self(Cow::Borrowed(&[
PositionEncoding::Utf8,
PositionEncoding::Utf16,
]));
pub fn new(encodings: Vec<PositionEncoding>) -> Result<Self, InvalidPositionEncodings> {
if encodings.is_empty() {
return Err(InvalidPositionEncodings::Empty);
}
Ok(Self(Cow::Owned(encodings)))
}
#[must_use]
pub fn as_slice(&self) -> &[PositionEncoding] {
&self.0
}
}
impl Default for PositionEncodings {
fn default() -> Self {
Self::DEFAULT
}
}
impl TryFrom<Vec<PositionEncoding>> for PositionEncodings {
type Error = InvalidPositionEncodings;
fn try_from(encodings: Vec<PositionEncoding>) -> Result<Self, Self::Error> {
Self::new(encodings)
}
}
impl From<PositionEncodings> for Vec<PositionEncoding> {
fn from(encodings: PositionEncodings) -> Self {
encodings.0.into_owned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Holder {
encodings: PositionEncodings,
}
#[test]
fn test_rejects_empty() {
assert_eq!(
PositionEncodings::new(vec![]),
Err(InvalidPositionEncodings::Empty)
);
assert!(toml::from_str::<Holder>("encodings = []").is_err());
}
#[test]
fn test_rejects_unknown_value_listing_valid_ones() {
let err = toml::from_str::<Holder>(r#"encodings = ["utf-7"]"#)
.unwrap_err()
.to_string();
assert!(err.contains("utf-7"), "{err}");
assert!(err.contains("utf-8") && err.contains("utf-16") && err.contains("utf-32"));
}
#[test]
fn test_preserves_order_and_round_trips() {
let holder: Holder = toml::from_str(r#"encodings = ["utf-32", "utf-8"]"#).unwrap();
assert_eq!(
holder.encodings.as_slice(),
[PositionEncoding::Utf32, PositionEncoding::Utf8]
);
let text = toml::to_string(&holder).unwrap();
assert_eq!(toml::from_str::<Holder>(&text).unwrap(), holder);
}
#[test]
fn test_default_round_trips() {
let holder = Holder {
encodings: PositionEncodings::DEFAULT,
};
let text = toml::to_string(&holder).unwrap();
assert_eq!(toml::from_str::<Holder>(&text).unwrap(), holder);
}
}