attribute_derive/
utils.rs1use from_partial::{FromPartial, Partial};
3use manyhow::SpanRanged;
4use parsing::{AttributeNamed, AttributeValue};
5use syn::token::Paren;
6use syn::Token;
7
8use self::parsing::parse_name;
9use crate::parsing::Named;
10use crate::{SpannedValue, *};
11
12#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
42pub enum FlagOrValue<T> {
43 #[default]
45 None,
46 Flag,
48 Value(T),
50}
51
52impl<T> FlagOrValue<T> {
53 pub fn is_none(&self) -> bool {
55 matches!(self, Self::None)
56 }
57
58 pub fn is_flag(&self) -> bool {
60 matches!(self, Self::Flag,)
61 }
62
63 pub fn is_value(&self) -> bool {
65 matches!(self, Self::Value(_),)
66 }
67
68 pub fn into_value(self) -> Option<T> {
70 match self {
71 FlagOrValue::Value(value) => Some(value),
72 _ => None,
73 }
74 }
75
76 pub fn as_value(&self) -> Option<&T> {
78 match self {
79 FlagOrValue::Value(value) => Some(value),
80 _ => None,
81 }
82 }
83
84 pub fn map_value<I>(self, map: impl FnOnce(T) -> I) -> FlagOrValue<I> {
86 match self {
87 FlagOrValue::None => FlagOrValue::None,
88 FlagOrValue::Flag => FlagOrValue::Flag,
89 FlagOrValue::Value(value) => FlagOrValue::Value(map(value)),
90 }
91 }
92}
93
94pub trait Transpose<T> {
97 fn transpose(self) -> T;
101}
102
103impl<T> Transpose<Option<FlagOrValue<T>>> for FlagOrValue<Option<T>> {
104 fn transpose(self) -> Option<FlagOrValue<T>> {
105 match self {
106 FlagOrValue::None => Some(FlagOrValue::None),
107 FlagOrValue::Flag => Some(FlagOrValue::Flag),
108 FlagOrValue::Value(option) => option.map(FlagOrValue::Value),
109 }
110 }
111}
112
113impl<T, E> Transpose<std::result::Result<FlagOrValue<T>, E>>
114 for FlagOrValue<std::result::Result<T, E>>
115{
116 fn transpose(self) -> std::result::Result<FlagOrValue<T>, E> {
117 match self {
118 FlagOrValue::None => Ok(FlagOrValue::None),
119 FlagOrValue::Flag => Ok(FlagOrValue::Flag),
120 FlagOrValue::Value(result) => result.map(FlagOrValue::Value),
121 }
122 }
123}
124
125impl<T: FromPartial<P>, P> FromPartial<Partial<FlagOrValue<P>>> for FlagOrValue<T> {
126 fn from(partial: Partial<FlagOrValue<P>>) -> syn::Result<Self> {
127 partial.0.map_value(T::from).transpose()
128 }
129
130 fn from_option(partial: Option<Partial<FlagOrValue<P>>>, _: &str) -> syn::Result<Self> {
131 partial
133 .map(FromPartial::from)
134 .transpose()
135 .map(Option::unwrap_or_default)
136 }
137
138 fn join(
139 first: Option<SpannedValue<Partial<FlagOrValue<P>>>>,
140 second: SpannedValue<Partial<FlagOrValue<P>>>,
141 specified_twice_error: &str,
142 ) -> syn::Result<Option<SpannedValue<Partial<FlagOrValue<P>>>>> {
143 match (first, second) {
144 (None, this) => Ok(Some(this)),
145 (
146 Some(value),
147 SpannedValue {
148 value: Partial(FlagOrValue::None | FlagOrValue::Flag),
149 ..
150 },
151 )
152 | (
153 Some(SpannedValue {
154 value: Partial(FlagOrValue::None | FlagOrValue::Flag),
155 ..
156 }),
157 value,
158 ) => Ok(Some(value)),
159 (Some(first), second) => P::join(
160 Some(first.map_value(|v| {
161 v.0.into_value()
162 .expect("flag and none are checked in earlier branch")
163 })),
164 second.map_value(|Partial(v)| {
165 v.into_value()
166 .expect("flag and none are checked in earlier branch")
167 }),
168 specified_twice_error,
169 )
170 .map(|o| o.map(|a| a.map_value(FlagOrValue::Value).map_value(Partial))),
171 }
172 }
173}
174
175impl<T: AttributeBase> AttributeBase for FlagOrValue<T> {
176 type Partial = Partial<FlagOrValue<T::Partial>>;
177}
178
179impl<T: AttributeValue> AttributeNamed for FlagOrValue<T> {
180 fn parse_named(
181 name: &'static str,
182 input: syn::parse::ParseStream,
183 ) -> syn::Result<Option<Named<SpannedValue<Self::Partial>>>> {
184 let Some(ident) = parse_name(input, name) else {
185 return Ok(None);
186 };
187 let value = if input.peek(Token![=]) {
188 T::parse_value_eq(input)?.map_value(FlagOrValue::Value)
189 } else if input.peek(Paren) {
190 T::parse_value_meta(input)?.map_value(FlagOrValue::Value)
191 } else {
192 SpannedValue {
193 value: FlagOrValue::Flag,
194 span: ident.span().span_range(),
195 }
196 }
197 .map_value(Partial);
198 Ok(Some(Named { value, name: ident }))
199 }
200}
201
202impl<T: FromAttr> FromAttr for FlagOrValue<T> {
203 fn parse_partial(input: ParseStream) -> Result<Self::Partial> {
204 if input.is_empty() {
205 Ok(Partial(FlagOrValue::Flag))
206 } else {
207 T::parse_partial(input).map(FlagOrValue::Value).map(Partial)
208 }
209 }
210}