amplify_syn/
val.rs

1// Rust language amplification derive library providing multiple generic trait
2// implementations, type wrappers, derive macros and other language enhancements
3//
4// Written in 2019-2021 by
5//     Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
6//
7// To the extent possible under law, the author(s) have dedicated all
8// copyright and related and neighboring rights to this software to
9// the public domain worldwide. This software is distributed without
10// any warranty.
11//
12// You should have received a copy of the MIT License
13// along with this software.
14// If not, see <https://opensource.org/licenses/MIT>.
15
16use std::convert::TryFrom;
17use std::fmt::{self, Debug, Formatter};
18
19use proc_macro2::Span;
20use quote::ToTokens;
21use syn::parse::{Parse, Parser};
22use syn::{
23    Expr, Ident, Lit, LitBool, LitByteStr, LitChar, LitFloat, LitInt, LitStr, Path, PathSegment,
24    Type, TypePath,
25};
26
27use crate::{Error, ValueClass};
28
29/// Value for attribute or attribute argument, i.e. for `#[attr = value]` and
30/// `#[attr(arg = value)]` this is the `value` part of the attribute. Can be
31/// either a single literal or a single valid rust type name
32#[derive(Clone)]
33pub enum ArgValue {
34    /// Attribute value represented by a literal
35    Literal(Lit),
36
37    /// Attribute value represented by a type name
38    Type(Type),
39
40    /// Attribute value represented by an expression
41    Expr(Expr),
42
43    /// No value is given
44    None,
45}
46
47impl Debug for ArgValue {
48    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
49        match self {
50            ArgValue::Literal(lit) => write!(f, "ArgValue::Literal({})", lit.to_token_stream()),
51            ArgValue::Type(ty) => write!(f, "ArgValue::Type({})", ty.to_token_stream()),
52            ArgValue::Expr(expr) => write!(f, "ArgValue::Expr({})", expr.to_token_stream()),
53            ArgValue::None => f.write_str("ArgValue::None"),
54        }
55    }
56}
57
58impl From<&str> for ArgValue {
59    fn from(val: &str) -> Self { ArgValue::Literal(Lit::Str(LitStr::new(val, Span::call_site()))) }
60}
61
62impl From<String> for ArgValue {
63    fn from(val: String) -> Self {
64        ArgValue::Literal(Lit::Str(LitStr::new(&val, Span::call_site())))
65    }
66}
67
68impl From<&[u8]> for ArgValue {
69    fn from(val: &[u8]) -> Self {
70        ArgValue::Literal(Lit::ByteStr(LitByteStr::new(val, Span::call_site())))
71    }
72}
73
74impl From<Vec<u8>> for ArgValue {
75    fn from(val: Vec<u8>) -> Self {
76        ArgValue::Literal(Lit::ByteStr(LitByteStr::new(&val, Span::call_site())))
77    }
78}
79
80impl From<char> for ArgValue {
81    fn from(val: char) -> Self {
82        ArgValue::Literal(Lit::Char(LitChar::new(val, Span::call_site())))
83    }
84}
85
86impl From<usize> for ArgValue {
87    fn from(val: usize) -> Self {
88        ArgValue::Literal(Lit::Int(LitInt::new(&val.to_string(), Span::call_site())))
89    }
90}
91
92impl From<isize> for ArgValue {
93    fn from(val: isize) -> Self {
94        ArgValue::Literal(Lit::Int(LitInt::new(&val.to_string(), Span::call_site())))
95    }
96}
97
98impl From<f64> for ArgValue {
99    fn from(val: f64) -> Self {
100        ArgValue::Literal(Lit::Float(LitFloat::new(&val.to_string(), Span::call_site())))
101    }
102}
103
104impl From<bool> for ArgValue {
105    fn from(val: bool) -> Self {
106        ArgValue::Literal(Lit::Bool(LitBool::new(val, Span::call_site())))
107    }
108}
109
110impl From<Option<LitStr>> for ArgValue {
111    fn from(val: Option<LitStr>) -> Self {
112        match val {
113            Some(val) => ArgValue::Literal(Lit::Str(val)),
114            None => ArgValue::None,
115        }
116    }
117}
118
119impl From<Ident> for ArgValue {
120    fn from(ident: Ident) -> Self {
121        Path::from(PathSegment::parse.parse2(quote! { #ident }.into()).unwrap()).into()
122    }
123}
124
125impl From<Path> for ArgValue {
126    fn from(path: Path) -> Self { ArgValue::Type(Type::Path(TypePath { qself: None, path })) }
127}
128
129impl From<Option<LitByteStr>> for ArgValue {
130    fn from(val: Option<LitByteStr>) -> Self {
131        match val {
132            Some(val) => ArgValue::Literal(Lit::ByteStr(val)),
133            None => ArgValue::None,
134        }
135    }
136}
137
138impl From<Option<LitBool>> for ArgValue {
139    fn from(val: Option<LitBool>) -> Self {
140        match val {
141            Some(val) => ArgValue::Literal(Lit::Bool(val)),
142            None => ArgValue::None,
143        }
144    }
145}
146
147impl From<Option<LitChar>> for ArgValue {
148    fn from(val: Option<LitChar>) -> Self {
149        match val {
150            Some(val) => ArgValue::Literal(Lit::Char(val)),
151            None => ArgValue::None,
152        }
153    }
154}
155
156impl From<Option<LitInt>> for ArgValue {
157    fn from(val: Option<LitInt>) -> Self {
158        match val {
159            Some(val) => ArgValue::Literal(Lit::Int(val)),
160            None => ArgValue::None,
161        }
162    }
163}
164
165impl From<Option<LitFloat>> for ArgValue {
166    fn from(val: Option<LitFloat>) -> Self {
167        match val {
168            Some(val) => ArgValue::Literal(Lit::Float(val)),
169            None => ArgValue::None,
170        }
171    }
172}
173
174impl TryFrom<ArgValue> for String {
175    type Error = Error;
176
177    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
178        match value {
179            ArgValue::Literal(Lit::Str(s)) => Ok(s.value()),
180            _ => Err(Error::ArgValueMustBeLiteral),
181        }
182    }
183}
184
185impl TryFrom<ArgValue> for Vec<u8> {
186    type Error = Error;
187
188    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
189        match value {
190            ArgValue::Literal(Lit::ByteStr(s)) => Ok(s.value()),
191            _ => Err(Error::ArgValueMustBeLiteral),
192        }
193    }
194}
195
196impl TryFrom<ArgValue> for bool {
197    type Error = Error;
198
199    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
200        match value {
201            ArgValue::Literal(Lit::Bool(b)) => Ok(b.value),
202            _ => Err(Error::ArgValueMustBeLiteral),
203        }
204    }
205}
206
207impl TryFrom<ArgValue> for char {
208    type Error = Error;
209
210    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
211        match value {
212            ArgValue::Literal(Lit::Char(c)) => Ok(c.value()),
213            _ => Err(Error::ArgValueMustBeLiteral),
214        }
215    }
216}
217
218impl TryFrom<ArgValue> for LitStr {
219    type Error = Error;
220
221    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
222        match value {
223            ArgValue::Literal(Lit::Str(s)) => Ok(s),
224            _ => Err(Error::ArgValueMustBeLiteral),
225        }
226    }
227}
228
229impl TryFrom<ArgValue> for LitByteStr {
230    type Error = Error;
231
232    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
233        match value {
234            ArgValue::Literal(Lit::ByteStr(s)) => Ok(s),
235            _ => Err(Error::ArgValueMustBeLiteral),
236        }
237    }
238}
239
240impl TryFrom<ArgValue> for LitBool {
241    type Error = Error;
242
243    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
244        match value {
245            ArgValue::Literal(Lit::Bool(s)) => Ok(s),
246            _ => Err(Error::ArgValueMustBeLiteral),
247        }
248    }
249}
250
251impl TryFrom<ArgValue> for LitChar {
252    type Error = Error;
253
254    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
255        match value {
256            ArgValue::Literal(Lit::Char(c)) => Ok(c),
257            _ => Err(Error::ArgValueMustBeLiteral),
258        }
259    }
260}
261
262impl TryFrom<ArgValue> for LitInt {
263    type Error = Error;
264
265    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
266        match value {
267            ArgValue::Literal(Lit::Int(i)) => Ok(i),
268            _ => Err(Error::ArgValueMustBeLiteral),
269        }
270    }
271}
272
273impl TryFrom<ArgValue> for LitFloat {
274    type Error = Error;
275
276    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
277        match value {
278            ArgValue::Literal(Lit::Float(f)) => Ok(f),
279            _ => Err(Error::ArgValueMustBeLiteral),
280        }
281    }
282}
283
284impl TryFrom<ArgValue> for Ident {
285    type Error = Error;
286
287    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
288        match value {
289            ArgValue::Type(Type::Path(ty)) => {
290                if let Some(ident) = ty.path.get_ident() {
291                    Ok(ident.clone())
292                } else {
293                    Err(Error::ArgValueMustBeType)
294                }
295            }
296            _ => Err(Error::ArgValueMustBeType),
297        }
298    }
299}
300
301impl TryFrom<ArgValue> for Path {
302    type Error = Error;
303
304    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
305        match value {
306            ArgValue::Expr(expr) => Path::parse
307                .parse2(expr.to_token_stream().into())
308                .map_err(Error::from),
309            ArgValue::Type(Type::Path(ty)) => Ok(ty.path),
310            _ => Err(Error::ArgValueMustBeType),
311        }
312    }
313}
314
315impl TryFrom<ArgValue> for Expr {
316    type Error = Error;
317
318    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
319        match value {
320            ArgValue::Literal(lit) => Expr::parse
321                .parse2(lit.to_token_stream().into())
322                .map_err(Error::from),
323            ArgValue::Type(ty) => Expr::parse
324                .parse2(ty.to_token_stream().into())
325                .map_err(Error::from),
326            ArgValue::Expr(expr) => Ok(expr),
327            ArgValue::None => Err(Error::ArgValueMustBeExpr),
328        }
329    }
330}
331
332impl TryFrom<ArgValue> for Option<LitStr> {
333    type Error = Error;
334
335    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
336        match value {
337            ArgValue::Literal(Lit::Str(s)) => Ok(Some(s)),
338            ArgValue::None => Ok(None),
339            _ => Err(Error::ArgValueMustBeLiteral),
340        }
341    }
342}
343
344impl TryFrom<ArgValue> for Option<LitByteStr> {
345    type Error = Error;
346
347    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
348        match value {
349            ArgValue::Literal(Lit::ByteStr(s)) => Ok(Some(s)),
350            ArgValue::None => Ok(None),
351            _ => Err(Error::ArgValueMustBeLiteral),
352        }
353    }
354}
355
356impl TryFrom<ArgValue> for Option<LitBool> {
357    type Error = Error;
358
359    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
360        match value {
361            ArgValue::Literal(Lit::Bool(b)) => Ok(Some(b)),
362            ArgValue::None => Ok(None),
363            _ => Err(Error::ArgValueMustBeLiteral),
364        }
365    }
366}
367
368impl TryFrom<ArgValue> for Option<LitChar> {
369    type Error = Error;
370
371    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
372        match value {
373            ArgValue::Literal(Lit::Char(c)) => Ok(Some(c)),
374            ArgValue::None => Ok(None),
375            _ => Err(Error::ArgValueMustBeLiteral),
376        }
377    }
378}
379
380impl TryFrom<ArgValue> for Option<LitInt> {
381    type Error = Error;
382
383    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
384        match value {
385            ArgValue::Literal(Lit::Int(i)) => Ok(Some(i)),
386            ArgValue::None => Ok(None),
387            _ => Err(Error::ArgValueMustBeLiteral),
388        }
389    }
390}
391
392impl TryFrom<ArgValue> for Option<LitFloat> {
393    type Error = Error;
394
395    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
396        match value {
397            ArgValue::Literal(Lit::Float(f)) => Ok(Some(f)),
398            ArgValue::None => Ok(None),
399            _ => Err(Error::ArgValueMustBeLiteral),
400        }
401    }
402}
403
404impl TryFrom<ArgValue> for Option<Ident> {
405    type Error = Error;
406
407    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
408        match value {
409            ArgValue::Type(Type::Path(ty)) => {
410                if let Some(ident) = ty.path.get_ident() {
411                    Ok(Some(ident.clone()))
412                } else {
413                    Err(Error::ArgValueMustBeType)
414                }
415            }
416            ArgValue::None => Ok(None),
417            _ => Err(Error::ArgValueMustBeType),
418        }
419    }
420}
421
422impl TryFrom<ArgValue> for Option<Path> {
423    type Error = Error;
424
425    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
426        match value {
427            ArgValue::Type(Type::Path(ty)) => Ok(Some(ty.path)),
428            ArgValue::Expr(expr) => Some(
429                Path::parse
430                    .parse2(expr.into_token_stream().into())
431                    .map_err(Error::from),
432            )
433            .transpose(),
434            ArgValue::None => Ok(None),
435            _ => Err(Error::ArgValueMustBeType),
436        }
437    }
438}
439
440impl TryFrom<ArgValue> for Option<Expr> {
441    type Error = Error;
442
443    fn try_from(value: ArgValue) -> Result<Self, Self::Error> {
444        match value {
445            ArgValue::Expr(expr) => Ok(Some(expr)),
446            ArgValue::Type(ty) => Some(
447                Expr::parse
448                    .parse2(ty.into_token_stream().into())
449                    .map_err(Error::from),
450            )
451            .transpose(),
452            ArgValue::Literal(lit) => Some(
453                Expr::parse
454                    .parse2(lit.into_token_stream().into())
455                    .map_err(Error::from),
456            )
457            .transpose(),
458            ArgValue::None => Ok(None),
459        }
460    }
461}
462
463impl ArgValue {
464    /// Converts into literal value for [`ArgValue::Literal`] variant or fails
465    /// with [`Error::ArgValueMustBeLiteral`] otherwise
466    #[inline]
467    pub fn into_literal_value(self) -> Result<Lit, Error> {
468        match self {
469            ArgValue::Literal(lit) => Ok(lit),
470            ArgValue::Type(_) | ArgValue::Expr(_) | ArgValue::None => {
471                Err(Error::ArgValueMustBeLiteral)
472            }
473        }
474    }
475
476    /// Converts into type value for [`ArgValue::Type`] variant or fails with
477    /// [`Error::ArgValueMustBeType`] otherwise
478    #[inline]
479    pub fn into_type_value(self) -> Result<Type, Error> {
480        match self {
481            ArgValue::Literal(_) | ArgValue::Expr(_) | ArgValue::None => {
482                Err(Error::ArgValueMustBeType)
483            }
484            ArgValue::Type(ty) => Ok(ty),
485        }
486    }
487
488    /// Converts into type value for [`ArgValue::Expr`] variant or fails with
489    /// [`Error::ArgValueMustBeExpr`] otherwise
490    #[inline]
491    pub fn into_type_expr(self) -> Result<Expr, Error> {
492        match self {
493            ArgValue::Literal(_) | ArgValue::Type(_) | ArgValue::None => {
494                Err(Error::ArgValueMustBeExpr)
495            }
496            ArgValue::Expr(expr) => Ok(expr),
497        }
498    }
499
500    /// Constructs literal value for [`ArgValue::Literal`] variant or fails with
501    /// [`Error::ArgValueMustBeLiteral`] otherwise
502    #[inline]
503    pub fn to_literal_value(&self) -> Result<Lit, Error> {
504        match self {
505            ArgValue::Literal(lit) => Ok(lit.clone()),
506            ArgValue::Type(_) | ArgValue::Expr(_) | ArgValue::None => {
507                Err(Error::ArgValueMustBeLiteral)
508            }
509        }
510    }
511
512    /// Constructs type value for [`ArgValue::Type`] variant or fails with
513    /// [`Error::ArgValueMustBeType`] otherwise
514    #[inline]
515    pub fn to_type_value(&self) -> Result<Type, Error> {
516        match self {
517            ArgValue::Literal(_) | ArgValue::Expr(_) | ArgValue::None => {
518                Err(Error::ArgValueMustBeType)
519            }
520            ArgValue::Type(ty) => Ok(ty.clone()),
521        }
522    }
523
524    /// Constructs type value for [`ArgValue::Expr`] variant or fails with
525    /// [`Error::ArgValueMustBeExpr`] otherwise
526    #[inline]
527    pub fn to_type_expr(&self) -> Result<Expr, Error> {
528        match self {
529            ArgValue::Literal(_) | ArgValue::Type(_) | ArgValue::None => {
530                Err(Error::ArgValueMustBeExpr)
531            }
532            ArgValue::Expr(expr) => Ok(expr.clone()),
533        }
534    }
535
536    /// Returns a reference to a literal value for [`ArgValue::Literal`]
537    /// variant, or `None` otherwise.
538    #[inline]
539    pub fn as_literal_value(&self) -> Option<&Lit> {
540        match self {
541            ArgValue::Literal(ref lit) => Some(lit),
542            ArgValue::Type(_) | ArgValue::Expr(_) | ArgValue::None => None,
543        }
544    }
545
546    /// Returns a reference to a literal value for  [`ArgValue::Type`]
547    /// variant, or `None` otherwise.
548    #[inline]
549    pub fn as_type_value(&self) -> Option<&Type> {
550        match self {
551            ArgValue::Literal(_) | ArgValue::Expr(_) | ArgValue::None => None,
552            ArgValue::Type(ref ty) => Some(ty),
553        }
554    }
555
556    /// Returns a reference to a literal value for  [`ArgValue::Expr`]
557    /// variant, or `None` otherwise.
558    #[inline]
559    pub fn as_type_expr(&self) -> Option<&Expr> {
560        match self {
561            ArgValue::Literal(_) | ArgValue::Type(_) | ArgValue::None => None,
562            ArgValue::Expr(ref expr) => Some(expr),
563        }
564    }
565
566    /// Tests whether the self is set to [`ArgValue::None`]
567    #[inline]
568    pub fn is_none(&self) -> bool {
569        #[allow(clippy::match_like_matches_macro)]
570        // Ancient rust versions do not known about `matches!` macro
571        match self {
572            ArgValue::None => true,
573            _ => false,
574        }
575    }
576
577    /// Tests whether the self is not set to [`ArgValue::None`]
578    #[inline]
579    pub fn is_some(&self) -> bool {
580        // Ancient rust versions do not known about `matches!` macro
581        #[allow(clippy::match_like_matches_macro)]
582        match self {
583            ArgValue::None => false,
584            _ => true,
585        }
586    }
587
588    /// Returns [`ValueClass`] for the current value, if any
589    #[inline]
590    pub fn value_class(&self) -> Option<ValueClass> {
591        match self {
592            ArgValue::Literal(lit) => Some(ValueClass::from(lit)),
593            ArgValue::Type(ty) => Some(ValueClass::from(ty)),
594            ArgValue::Expr(_) => Some(ValueClass::Expr),
595            ArgValue::None => None,
596        }
597    }
598}