darling_core/util/
extract_option.rs1use crate::{Error, Result};
24use syn::{spanned::Spanned, Type};
25
26pub fn from_owned(ty: Type) -> Result<Type> {
48 let span = ty.span();
49 let err = || Error::custom("expected an `Option`").with_span(&span);
50
51 let Type::Path(path) = ty else {
52 return Err(err());
53 };
54
55 if path.qself.is_some() {
56 return Err(err());
57 }
58
59 let Some(last_segment) = path.path.segments.last() else {
60 return Err(err());
61 };
62
63 if last_segment.ident != "Option" {
64 return Err(err());
65 }
66
67 let syn::PathArguments::AngleBracketed(ty) = last_segment.clone().arguments else {
68 return Err(err());
69 };
70
71 let args = ty.args.into_iter().collect::<Vec<_>>();
72
73 if args.len() != 1 {
74 return Err(err());
75 }
76
77 let arg = args
78 .into_iter()
79 .next()
80 .expect("just checked that `.len() == 1`");
81
82 let syn::GenericArgument::Type(ty) = arg else {
83 return Err(err());
84 };
85
86 Ok(ty)
87}
88
89pub fn from_mut(ty: &mut Type) -> Result<&mut Type> {
111 let span = ty.span();
112 let err = || Error::custom("expected an `Option`").with_span(&span);
113
114 let Type::Path(path) = ty else {
115 return Err(err());
116 };
117
118 if path.qself.is_some() {
119 return Err(err());
120 }
121
122 let Some(last_segment) = path.path.segments.last_mut() else {
123 return Err(err());
124 };
125
126 if last_segment.ident != "Option" {
127 return Err(err());
128 }
129
130 let syn::PathArguments::AngleBracketed(ty) = &mut last_segment.arguments else {
131 return Err(err());
132 };
133
134 let args = ty.args.iter_mut().collect::<Vec<_>>();
135
136 if args.len() != 1 {
137 return Err(err());
138 }
139
140 let arg = args
141 .into_iter()
142 .next()
143 .expect("just checked that `.len() == 1`");
144
145 let syn::GenericArgument::Type(ty) = arg else {
146 return Err(err());
147 };
148
149 Ok(ty)
150}
151
152pub fn from_ref(ty: &Type) -> Result<&Type> {
174 let span = ty.span();
175 let err = || Error::custom("expected an `Option`").with_span(&span);
176
177 let Type::Path(path) = ty else {
178 return Err(err());
179 };
180
181 if path.qself.is_some() {
182 return Err(err());
183 }
184
185 let Some(last_segment) = &path.path.segments.last() else {
186 return Err(err());
187 };
188
189 if &last_segment.ident != "Option" {
190 return Err(err());
191 }
192
193 let syn::PathArguments::AngleBracketed(ty) = &last_segment.arguments else {
194 return Err(err());
195 };
196
197 let args = ty.args.iter().collect::<Vec<_>>();
198
199 if args.len() != 1 {
200 return Err(err());
201 }
202
203 let arg = args
204 .into_iter()
205 .next()
206 .expect("just checked that `.len() == 1`");
207
208 let syn::GenericArgument::Type(ty) = arg else {
209 return Err(err());
210 };
211
212 Ok(ty)
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218 use syn::Type;
219
220 macro_rules! test_all {
221 ($test:literal, $method:ident) => {
222 from_owned(syn::parse_str::<Type>($test).unwrap()).$method();
223 from_ref(&syn::parse_str::<Type>($test).unwrap()).$method();
224 from_mut(&mut syn::parse_str::<Type>($test).unwrap()).$method();
225 };
226 }
227
228 #[test]
231 fn simple() {
232 test_all!("Option<String>", unwrap);
233 }
234
235 #[test]
236 fn fully_qualified() {
237 test_all!("std::option::Option<String>", unwrap);
238 test_all!("core::option::Option<String>", unwrap);
239 }
240
241 #[test]
242 fn absolute_path() {
243 test_all!("::std::option::Option<String>", unwrap);
244 test_all!("::core::option::Option<String>", unwrap);
245 }
246
247 #[test]
248 fn submodule() {
249 test_all!("option::Option<String>", unwrap);
250 }
251
252 #[test]
255 fn wrong_arg_count() {
256 test_all!("Option<String, u8>", unwrap_err);
257 }
258
259 #[test]
260 fn rejects_qself() {
261 test_all!("<T as Option>::Option<u32>", unwrap_err);
262 }
263
264 #[test]
265 fn non_path() {
266 test_all!("&'a Option<u8>", unwrap_err);
267 }
268}