1#[cfg(feature = "span")]
2use miette::SourceSpan;
3use std::{fmt::Display, str::FromStr};
4
5use crate::{v2_parser, KdlError, KdlValue};
6
7#[derive(Debug, Clone, Eq)]
10pub struct KdlIdentifier {
11 pub(crate) value: String,
12 pub(crate) repr: Option<String>,
13 #[cfg(feature = "span")]
14 pub(crate) span: SourceSpan,
15}
16
17impl PartialEq for KdlIdentifier {
18 fn eq(&self, other: &Self) -> bool {
19 self.value == other.value && self.repr == other.repr
20 }
22}
23
24impl std::hash::Hash for KdlIdentifier {
25 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
26 self.value.hash(state);
27 self.repr.hash(state);
28 }
30}
31
32impl KdlIdentifier {
33 pub fn value(&self) -> &str {
35 &self.value
36 }
37
38 pub fn set_value(&mut self, value: impl Into<String>) {
40 self.value = value.into();
41 }
42
43 #[cfg(feature = "span")]
49 pub fn span(&self) -> SourceSpan {
50 self.span
51 }
52
53 #[cfg(feature = "span")]
55 pub fn set_span(&mut self, span: impl Into<SourceSpan>) {
56 self.span = span.into();
57 }
58
59 pub fn repr(&self) -> Option<&str> {
61 self.repr.as_deref()
62 }
63
64 pub fn set_repr(&mut self, repr: impl Into<String>) {
66 self.repr = Some(repr.into());
67 }
68
69 pub fn len(&self) -> usize {
71 format!("{self}").len()
72 }
73
74 pub fn is_empty(&self) -> bool {
76 self.len() == 0
77 }
78
79 pub fn clear_format(&mut self) {
83 self.repr = None;
84 }
85
86 pub fn autoformat(&mut self) {
88 self.repr = None;
89 }
90
91 pub fn parse(s: &str) -> Result<Self, KdlError> {
98 #[cfg(not(feature = "v1-fallback"))]
99 {
100 v2_parser::try_parse(v2_parser::identifier, s)
101 }
102 #[cfg(feature = "v1-fallback")]
103 {
104 v2_parser::try_parse(v2_parser::identifier, s)
105 .or_else(|e| KdlIdentifier::parse_v1(s).map_err(|_| e))
106 }
107 }
108
109 #[cfg(feature = "v1")]
111 pub fn parse_v1(s: &str) -> Result<Self, KdlError> {
112 let ret: Result<kdlv1::KdlIdentifier, kdlv1::KdlError> = s.parse();
113 ret.map(|x| x.into()).map_err(|e| e.into())
114 }
115}
116
117#[cfg(feature = "v1")]
118impl From<kdlv1::KdlIdentifier> for KdlIdentifier {
119 fn from(value: kdlv1::KdlIdentifier) -> Self {
120 Self {
121 value: value.value().into(),
122 repr: value.repr().map(|x| x.into()),
123 #[cfg(feature = "span")]
124 span: (value.span().offset(), value.span().len()).into(),
125 }
126 }
127}
128
129impl Display for KdlIdentifier {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 if let Some(repr) = &self.repr {
132 write!(f, "{repr}")
133 } else {
134 write!(f, "{}", KdlValue::String(self.value().into()))
135 }
136 }
137}
138
139impl From<&str> for KdlIdentifier {
140 fn from(value: &str) -> Self {
141 Self {
142 value: value.to_string(),
143 repr: None,
144 #[cfg(feature = "span")]
145 span: SourceSpan::from(0..0),
146 }
147 }
148}
149
150impl From<String> for KdlIdentifier {
151 fn from(value: String) -> Self {
152 Self {
153 value,
154 repr: None,
155 #[cfg(feature = "span")]
156 span: SourceSpan::from(0..0),
157 }
158 }
159}
160
161impl From<KdlIdentifier> for String {
162 fn from(value: KdlIdentifier) -> Self {
163 value.value
164 }
165}
166
167impl FromStr for KdlIdentifier {
168 type Err = KdlError;
169
170 fn from_str(s: &str) -> Result<Self, Self::Err> {
171 Self::parse(s)
172 }
173}
174
175#[cfg(test)]
176mod test {
177 use super::*;
178
179 #[test]
180 fn parsing() -> miette::Result<()> {
181 let plain = "foo";
182 assert_eq!(
183 plain.parse::<KdlIdentifier>()?,
184 KdlIdentifier {
185 value: plain.to_string(),
186 repr: Some(plain.to_string()),
187 #[cfg(feature = "span")]
188 span: SourceSpan::from(0..3),
189 }
190 );
191
192 let quoted = r#""foo\"bar""#;
193 assert_eq!(
194 quoted.parse::<KdlIdentifier>()?,
195 KdlIdentifier {
196 value: "foo\"bar".to_string(),
197 repr: Some(quoted.to_string()),
198 #[cfg(feature = "span")]
199 span: SourceSpan::from(0..0),
200 }
201 );
202
203 let invalid = "123";
204 assert!(invalid.parse::<KdlIdentifier>().is_err());
205
206 let invalid = " space ";
207 assert!(invalid.parse::<KdlIdentifier>().is_err());
208
209 let invalid = "\"x";
210 assert!(invalid.parse::<KdlIdentifier>().is_err());
211
212 Ok(())
213 }
214
215 #[test]
216 fn formatting() {
217 let plain = KdlIdentifier::from("foo");
218 assert_eq!(format!("{plain}"), "foo");
219
220 let quoted = KdlIdentifier::from("foo\"bar");
221 assert_eq!(format!("{quoted}"), r#""foo\"bar""#);
222
223 let mut custom_repr = KdlIdentifier::from("foo");
224 custom_repr.set_repr(r#""foo/bar""#.to_string());
225 assert_eq!(format!("{custom_repr}"), r#""foo/bar""#);
226 }
227}