introspect_core/enum/
variant.rs1mod builder;
4
5pub use builder::Builder;
6
7pub enum Error {
9 UnsupportedExpression(syn::Expr),
11
12 UnsupportedExpressionLiteral(syn::ExprLit),
14}
15
16impl std::fmt::Debug for Error {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 match self {
19 Self::UnsupportedExpression(_) => f.debug_tuple("UnsupportedExpression").finish(),
20 Self::UnsupportedExpressionLiteral(_) => {
21 f.debug_tuple("UnsupportedExpressionLiteral").finish()
22 }
23 }
24 }
25}
26
27impl std::fmt::Display for Error {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 Error::UnsupportedExpression(_) => {
31 write!(f, "unsupported doc attribute expression")
32 }
33 Error::UnsupportedExpressionLiteral(_) => {
34 write!(f, "unsupported doc attribute literal")
35 }
36 }
37 }
38}
39
40impl std::error::Error for Error {}
41
42pub type Result<T> = std::result::Result<T, Error>;
44
45#[derive(Debug)]
47pub struct Variant {
48 identifier: String,
50
51 documentation: Option<String>,
53}
54
55impl Variant {
56 pub fn new(identifier: String, documentation: Option<String>) -> Self {
69 Self {
70 identifier,
71 documentation,
72 }
73 }
74
75 pub fn identifier(&self) -> &str {
92 self.identifier.as_str()
93 }
94
95 pub fn documentation(&self) -> Option<&str> {
112 self.documentation.as_deref()
113 }
114}
115
116impl std::fmt::Display for Variant {
117 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118 write!(f, "::introspect::r#enum::Variant::new(")?;
119 write!(f, "r#\"{}\"#.into(), ", self.identifier)?;
120
121 match self.documentation.as_ref() {
122 Some(documentation) => write!(f, "Some(r#\"{}\"#.into())", documentation)?,
123 None => write!(f, "None")?,
124 };
125
126 write!(f, ")")
127 }
128}
129
130impl TryFrom<&syn::Variant> for Variant {
131 type Error = Error;
132
133 fn try_from(value: &syn::Variant) -> Result<Self> {
134 let documentation = value
135 .attrs
136 .iter()
137 .filter_map(|attr| match attr.meta.require_name_value() {
138 Ok(v) => Some(v),
139 Err(_) => None,
140 })
141 .filter_map(|variant| {
142 variant
143 .path
144 .get_ident()
145 .map(|ident| (ident, variant.value.clone()))
146 })
147 .filter(|(ident, _)| *ident == "doc")
148 .map(|(_, expr)| match expr {
149 syn::Expr::Lit(expr_lit) => match expr_lit.lit {
150 syn::Lit::Str(lit_str) => Ok(lit_str.value().trim().to_string()),
151 _ => Err(Error::UnsupportedExpressionLiteral(expr_lit)),
152 },
153 _ => Err(Error::UnsupportedExpression(expr)),
154 })
155 .collect::<Result<Vec<String>>>()?
156 .join("\n");
157
158 Ok(Self {
159 identifier: value.ident.to_string(),
160 documentation: match documentation.is_empty() {
161 true => None,
162 false => Some(documentation),
163 },
164 })
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171
172 #[test]
173 fn display_identifier_and_documentation() {
174 let variant = Variant::new(String::from("Name"), Some(String::from("Documentation.")));
175
176 assert_eq!(
177 variant.to_string(),
178 "::introspect::r#enum::Variant::new(r#\"Name\"#.into(), Some(r#\"Documentation.\"#.into()))"
179 )
180 }
181
182 #[test]
183 fn display_only_identifier() {
184 let variant = Variant::new(String::from("Name"), None);
185
186 assert_eq!(
187 variant.to_string(),
188 "::introspect::r#enum::Variant::new(r#\"Name\"#.into(), None)"
189 )
190 }
191}