ion_proc/attribute/
name.rs

1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 */
6
7use convert_case::{Case, Casing as _};
8use proc_macro2::{Ident, Span, TokenStream};
9use quote::format_ident;
10use syn::parse::{Parse, ParseStream};
11use syn::{Error, Expr, ExprLit, ExprPath, Lit, LitStr, parse_quote};
12
13#[derive(Clone)]
14pub(crate) enum Name {
15	String(LitStr),
16	Symbol(ExprPath),
17}
18
19impl Name {
20	pub(crate) fn from_string<S: AsRef<str>>(string: S, span: Span) -> Name {
21		Name::String(LitStr::new(string.as_ref(), span))
22	}
23
24	pub(crate) fn as_string(&self) -> String {
25		match self {
26			Name::String(literal) => literal.value(),
27			Name::Symbol(path) => path.path.segments.last().map(|segment| format!("[{}]", segment.ident)).unwrap(),
28		}
29	}
30
31	pub(crate) fn to_property_spec(&self, ion: &TokenStream, function: &mut Ident) -> (Box<Expr>, Box<Expr>) {
32		match self {
33			Name::String(literal) => {
34				let mut name = literal.value();
35				if name.is_case(Case::UpperSnake) {
36					name = name.to_case(Case::Camel);
37				}
38				(
39					Box::new(Expr::Lit(ExprLit {
40						attrs: Vec::new(),
41						lit: Lit::Str(LitStr::new(&name, literal.span())),
42					})),
43					parse_quote!(#ion::flags::PropertyFlags::CONSTANT_ENUMERATED),
44				)
45			}
46			Name::Symbol(symbol) => {
47				*function = format_ident!("{}_symbol", function);
48				(
49					Box::new(Expr::Path(symbol.clone())),
50					parse_quote!(#ion::flags::PropertyFlags::CONSTANT),
51				)
52			}
53		}
54	}
55}
56
57impl Parse for Name {
58	fn parse(input: ParseStream) -> syn::Result<Name> {
59		if let Ok(literal) = input.parse::<LitStr>() {
60			let string = literal.value();
61			if !string.starts_with('[') && !string.ends_with(']') {
62				Ok(Name::String(literal))
63			} else {
64				Err(Error::new(
65					literal.span(),
66					"Function name must not start with '[' or end with ']'",
67				))
68			}
69		} else if let Ok(other) = input.parse() {
70			Ok(Name::Symbol(other))
71		} else {
72			Err(Error::new(input.span(), "Function name is not a string or expression"))
73		}
74	}
75}