mcpls_core/config/
position_encodings.rs1use std::borrow::Cow;
4
5use serde::{Deserialize, Serialize};
6
7use crate::bridge::PositionEncoding;
8
9#[derive(thiserror::Error, Debug, Clone, Copy, PartialEq, Eq)]
11pub enum InvalidPositionEncodings {
12 #[error("position_encodings cannot be empty")]
14 Empty,
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(try_from = "Vec<PositionEncoding>", into = "Vec<PositionEncoding>")]
39pub struct PositionEncodings(Cow<'static, [PositionEncoding]>);
40
41impl PositionEncodings {
42 pub const DEFAULT: Self = Self(Cow::Borrowed(&[
48 PositionEncoding::Utf8,
49 PositionEncoding::Utf16,
50 ]));
51
52 pub fn new(encodings: Vec<PositionEncoding>) -> Result<Self, InvalidPositionEncodings> {
58 if encodings.is_empty() {
59 return Err(InvalidPositionEncodings::Empty);
60 }
61 Ok(Self(Cow::Owned(encodings)))
62 }
63
64 #[must_use]
66 pub fn as_slice(&self) -> &[PositionEncoding] {
67 &self.0
68 }
69}
70
71impl Default for PositionEncodings {
72 fn default() -> Self {
73 Self::DEFAULT
74 }
75}
76
77impl TryFrom<Vec<PositionEncoding>> for PositionEncodings {
78 type Error = InvalidPositionEncodings;
79
80 fn try_from(encodings: Vec<PositionEncoding>) -> Result<Self, Self::Error> {
81 Self::new(encodings)
82 }
83}
84
85impl From<PositionEncodings> for Vec<PositionEncoding> {
86 fn from(encodings: PositionEncodings) -> Self {
87 encodings.0.into_owned()
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[derive(Debug, Serialize, Deserialize, PartialEq)]
96 struct Holder {
97 encodings: PositionEncodings,
98 }
99
100 #[test]
101 fn test_rejects_empty() {
102 assert_eq!(
103 PositionEncodings::new(vec![]),
104 Err(InvalidPositionEncodings::Empty)
105 );
106 assert!(toml::from_str::<Holder>("encodings = []").is_err());
107 }
108
109 #[test]
110 fn test_rejects_unknown_value_listing_valid_ones() {
111 let err = toml::from_str::<Holder>(r#"encodings = ["utf-7"]"#)
112 .unwrap_err()
113 .to_string();
114 assert!(err.contains("utf-7"), "{err}");
115 assert!(err.contains("utf-8") && err.contains("utf-16") && err.contains("utf-32"));
116 }
117
118 #[test]
119 fn test_preserves_order_and_round_trips() {
120 let holder: Holder = toml::from_str(r#"encodings = ["utf-32", "utf-8"]"#).unwrap();
121 assert_eq!(
122 holder.encodings.as_slice(),
123 [PositionEncoding::Utf32, PositionEncoding::Utf8]
124 );
125 let text = toml::to_string(&holder).unwrap();
126 assert_eq!(toml::from_str::<Holder>(&text).unwrap(), holder);
127 }
128
129 #[test]
130 fn test_default_round_trips() {
131 let holder = Holder {
132 encodings: PositionEncodings::DEFAULT,
133 };
134 let text = toml::to_string(&holder).unwrap();
135 assert_eq!(toml::from_str::<Holder>(&text).unwrap(), holder);
136 }
137}