1use std::{collections::HashSet, ops::Deref};
6
7use thiserror::Error;
8
9use crate::{
10 parse::{Context, Parse},
11 text::simple_extensions,
12};
13
14#[derive(Clone, Debug, PartialEq)]
16pub enum ArgumentsItem {
17 EnumArgument(EnumerationArg),
20
21 ValueArgument(ValueArg),
23
24 TypeArgument(TypeArg),
27}
28
29impl ArgumentsItem {
30 #[inline]
33 fn parse_optional_string(
34 name: &str,
35 value: Option<String>,
36 ) -> Result<Option<String>, ArgumentsItemError> {
37 match value {
38 Some(s) if s.is_empty() => {
39 Err(ArgumentsItemError::EmptyOptionalField(name.to_string()))
40 }
41 _ => Ok(value),
42 }
43 }
44
45 #[inline]
46 fn parse_name(name: Option<String>) -> Result<Option<String>, ArgumentsItemError> {
47 ArgumentsItem::parse_optional_string("name", name)
48 }
49
50 #[inline]
51 fn parse_description(
52 description: Option<String>,
53 ) -> Result<Option<String>, ArgumentsItemError> {
54 ArgumentsItem::parse_optional_string("description", description)
55 }
56}
57
58impl<C: Context> Parse<C> for simple_extensions::ArgumentsItem {
59 type Parsed = ArgumentsItem;
60 type Error = ArgumentsItemError;
61
62 fn parse(self, ctx: &mut C) -> Result<Self::Parsed, Self::Error> {
63 match self {
64 simple_extensions::ArgumentsItem::EnumerationArg(arg) => Ok(ctx.parse(arg)?.into()),
65 simple_extensions::ArgumentsItem::ValueArg(arg) => Ok(ctx.parse(arg)?.into()),
66 simple_extensions::ArgumentsItem::TypeArg(arg) => Ok(ctx.parse(arg)?.into()),
67 }
68 }
69}
70
71impl From<ArgumentsItem> for simple_extensions::ArgumentsItem {
72 fn from(value: ArgumentsItem) -> Self {
73 match value {
74 ArgumentsItem::EnumArgument(arg) => arg.into(),
75 ArgumentsItem::ValueArgument(arg) => arg.into(),
76 ArgumentsItem::TypeArgument(arg) => arg.into(),
77 }
78 }
79}
80
81#[derive(Debug, Error, PartialEq)]
83pub enum ArgumentsItemError {
84 #[error("invalid enumeration options: {0}")]
86 InvalidEnumOptions(#[from] EnumOptionsError),
87
88 #[error("the optional field `{0}` is empty and should be removed")]
90 EmptyOptionalField(String),
91}
92
93#[derive(Clone, Debug, PartialEq)]
95pub struct EnumerationArg {
96 name: Option<String>,
98
99 description: Option<String>,
101
102 options: EnumOptions,
104}
105
106impl EnumerationArg {
107 pub fn name(&self) -> Option<&String> {
111 self.name.as_ref()
112 }
113
114 pub fn description(&self) -> Option<&String> {
118 self.description.as_ref()
119 }
120
121 pub fn options(&self) -> &EnumOptions {
125 &self.options
126 }
127}
128
129impl<C: Context> Parse<C> for simple_extensions::EnumerationArg {
130 type Parsed = EnumerationArg;
131
132 type Error = ArgumentsItemError;
133
134 fn parse(self, ctx: &mut C) -> Result<EnumerationArg, ArgumentsItemError> {
135 Ok(EnumerationArg {
136 name: ArgumentsItem::parse_name(self.name)?,
137 description: ArgumentsItem::parse_description(self.description)?,
138 options: ctx.parse(self.options)?,
139 })
140 }
141}
142
143impl From<EnumerationArg> for simple_extensions::EnumerationArg {
144 fn from(value: EnumerationArg) -> Self {
145 simple_extensions::EnumerationArg {
146 name: value.name,
147 description: value.description,
148 options: value.options.into(),
149 }
150 }
151}
152
153impl From<EnumerationArg> for simple_extensions::ArgumentsItem {
154 fn from(value: EnumerationArg) -> Self {
155 simple_extensions::ArgumentsItem::EnumerationArg(value.into())
156 }
157}
158
159impl From<EnumerationArg> for ArgumentsItem {
160 fn from(value: EnumerationArg) -> Self {
161 ArgumentsItem::EnumArgument(value)
162 }
163}
164
165#[derive(Clone, Debug, PartialEq)]
167pub struct EnumOptions(HashSet<String>);
168
169impl Deref for EnumOptions {
170 type Target = HashSet<String>;
171
172 fn deref(&self) -> &Self::Target {
173 &self.0
174 }
175}
176
177impl<C: Context> Parse<C> for simple_extensions::EnumOptions {
178 type Parsed = EnumOptions;
179
180 type Error = EnumOptionsError;
181
182 fn parse(self, _ctx: &mut C) -> Result<EnumOptions, EnumOptionsError> {
183 self.try_into()
184 }
185}
186
187impl TryFrom<simple_extensions::EnumOptions> for EnumOptions {
188 type Error = EnumOptionsError;
189
190 fn try_from(raw: simple_extensions::EnumOptions) -> Result<Self, Self::Error> {
191 let options = raw.0;
192 if options.is_empty() {
193 return Err(EnumOptionsError::EmptyList);
194 }
195
196 let mut unique_options = HashSet::new();
197 for option in options.into_iter() {
198 if option.is_empty() {
199 return Err(EnumOptionsError::EmptyOption);
200 }
201 if !unique_options.insert(option.clone()) {
202 return Err(EnumOptionsError::DuplicatedOption(option));
203 }
204 }
205
206 Ok(EnumOptions(unique_options))
207 }
208}
209
210impl From<EnumOptions> for simple_extensions::EnumOptions {
211 fn from(value: EnumOptions) -> Self {
212 simple_extensions::EnumOptions(Vec::from_iter(value.0))
213 }
214}
215
216#[derive(Debug, Error, PartialEq)]
218pub enum EnumOptionsError {
219 #[error("empty list")]
221 EmptyList,
222
223 #[error("duplicated option: {0}")]
225 DuplicatedOption(String),
226
227 #[error("empty option")]
229 EmptyOption,
230}
231
232#[derive(Clone, Debug, PartialEq)]
234pub struct ValueArg {
235 name: Option<String>,
237
238 description: Option<String>,
240
241 value: simple_extensions::Type,
245
246 constant: Option<bool>,
250}
251
252impl ValueArg {
253 pub fn name(&self) -> Option<&String> {
257 self.name.as_ref()
258 }
259
260 pub fn description(&self) -> Option<&String> {
264 self.description.as_ref()
265 }
266
267 pub fn constant(&self) -> bool {
272 self.constant.unwrap_or(false)
273 }
274}
275
276impl<C: Context> Parse<C> for simple_extensions::ValueArg {
277 type Parsed = ValueArg;
278
279 type Error = ArgumentsItemError;
280
281 fn parse(self, _ctx: &mut C) -> Result<ValueArg, ArgumentsItemError> {
282 Ok(ValueArg {
283 name: ArgumentsItem::parse_name(self.name)?,
284 description: ArgumentsItem::parse_description(self.description)?,
285 value: self.value,
286 constant: self.constant,
287 })
288 }
289}
290
291impl From<ValueArg> for simple_extensions::ValueArg {
292 fn from(value: ValueArg) -> Self {
293 simple_extensions::ValueArg {
294 name: value.name,
295 description: value.description,
296 value: value.value,
297 constant: value.constant,
298 }
299 }
300}
301
302impl From<ValueArg> for simple_extensions::ArgumentsItem {
303 fn from(value: ValueArg) -> Self {
304 simple_extensions::ArgumentsItem::ValueArg(value.into())
305 }
306}
307
308impl From<ValueArg> for ArgumentsItem {
309 fn from(value: ValueArg) -> Self {
310 ArgumentsItem::ValueArgument(value)
311 }
312}
313
314#[derive(Clone, Debug, PartialEq)]
316pub struct TypeArg {
317 name: Option<String>,
319
320 description: Option<String>,
322
323 type_: String,
327}
328
329impl TypeArg {
330 pub fn name(&self) -> Option<&String> {
334 self.name.as_ref()
335 }
336
337 pub fn description(&self) -> Option<&String> {
341 self.description.as_ref()
342 }
343}
344
345impl<C: Context> Parse<C> for simple_extensions::TypeArg {
346 type Parsed = TypeArg;
347
348 type Error = ArgumentsItemError;
349
350 fn parse(self, _ctx: &mut C) -> Result<TypeArg, ArgumentsItemError> {
351 Ok(TypeArg {
352 name: ArgumentsItem::parse_name(self.name)?,
353 description: ArgumentsItem::parse_description(self.description)?,
354 type_: self.type_,
355 })
356 }
357}
358
359impl From<TypeArg> for simple_extensions::TypeArg {
360 fn from(value: TypeArg) -> Self {
361 simple_extensions::TypeArg {
362 name: value.name,
363 description: value.description,
364 type_: value.type_,
365 }
366 }
367}
368
369impl From<TypeArg> for simple_extensions::ArgumentsItem {
370 fn from(value: TypeArg) -> Self {
371 simple_extensions::ArgumentsItem::TypeArg(value.into())
372 }
373}
374
375impl From<TypeArg> for ArgumentsItem {
376 fn from(value: TypeArg) -> Self {
377 ArgumentsItem::TypeArgument(value)
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384 use crate::text::simple_extensions;
385 use crate::{parse::Context, text};
386
387 pub struct TestContext;
389
390 impl Context for TestContext {}
391
392 #[test]
393 fn parse_enum_argument() -> Result<(), ArgumentsItemError> {
394 let enum_argument =
395 simple_extensions::ArgumentsItem::EnumerationArg(simple_extensions::EnumerationArg {
396 name: Some("arg".to_string()),
397 description: Some("desc".to_string()),
398 options: simple_extensions::EnumOptions(vec!["OVERFLOW".to_string()]),
399 });
400 let item = enum_argument.parse(&mut TestContext)?;
401 let enum_argument = match item {
402 ArgumentsItem::EnumArgument(enum_argument) => enum_argument,
403 _ => unreachable!(),
404 };
405 assert_eq!(
406 enum_argument,
407 EnumerationArg {
408 name: Some("arg".to_string()),
409 description: Some("desc".to_string()),
410 options: EnumOptions(HashSet::from(["OVERFLOW".to_string()])),
411 }
412 );
413 Ok(())
414 }
415
416 #[test]
417 fn parse_empty_enum_options() -> Result<(), ArgumentsItemError> {
418 let options = simple_extensions::EnumOptions(vec![]);
419 let is_err = options
420 .parse(&mut TestContext)
421 .err()
422 .map(|err| matches!(err, EnumOptionsError::EmptyList));
423 assert_eq!(is_err, Some(true));
424 Ok(())
425 }
426
427 #[test]
428 fn parse_enum_options_with_empty_value() -> Result<(), ArgumentsItemError> {
429 let options = simple_extensions::EnumOptions(vec!["".to_string()]);
430 let is_err = options
431 .parse(&mut TestContext)
432 .err()
433 .map(|err| matches!(err, EnumOptionsError::EmptyOption));
434 assert_eq!(is_err, Some(true));
435 Ok(())
436 }
437
438 #[test]
439 fn parse_enum_argument_with_duplicated_option() -> Result<(), ArgumentsItemError> {
440 let options =
441 simple_extensions::EnumOptions(vec!["OVERFLOW".to_string(), "OVERFLOW".to_string()]);
442 let is_err = options.clone().parse(&mut TestContext).err().map(|err| {
443 matches!(
444 err,
445 EnumOptionsError::DuplicatedOption(opt) if opt == "OVERFLOW"
446 )
447 });
448 assert_eq!(is_err, Some(true));
449 Ok(())
450 }
451
452 #[test]
453 fn parse_value_argument() -> Result<(), ArgumentsItemError> {
454 let item = simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
455 name: Some("arg".to_string()),
456 description: Some("desc".to_string()),
457 value: text::simple_extensions::Type::String("i32".to_string()),
458 constant: Some(true),
459 });
460 let item = item.parse(&mut TestContext)?;
461 match item {
462 ArgumentsItem::ValueArgument(ValueArg {
463 name,
464 description,
465 value,
466 constant,
467 }) => {
468 assert_eq!(name, Some("arg".to_string()));
469 assert_eq!(description, Some("desc".to_string()));
470 assert!(
471 matches!(value, text::simple_extensions::Type::String(type_) if type_ == "i32")
472 );
473 assert_eq!(constant, Some(true));
474 }
475 _ => unreachable!(),
476 };
477 Ok(())
478 }
479
480 #[test]
481 fn parse_type_argument() -> Result<(), ArgumentsItemError> {
482 let type_argument = simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
483 name: Some("arg".to_string()),
484 description: Some("desc".to_string()),
485 type_: "".to_string(),
486 });
487 let item = type_argument.parse(&mut TestContext)?;
488 match item {
489 ArgumentsItem::TypeArgument(TypeArg {
490 name,
491 description,
492 type_,
493 }) => {
494 assert_eq!(name, Some("arg".to_string()));
495 assert_eq!(description, Some("desc".to_string()));
496 assert_eq!(type_, "");
497 }
498 _ => unreachable!(),
499 };
500 Ok(())
501 }
502
503 #[test]
504 fn parse_argument_with_nones() -> Result<(), ArgumentsItemError> {
505 let items = vec![
506 simple_extensions::ArgumentsItem::EnumerationArg(simple_extensions::EnumerationArg {
507 name: None,
508 description: None,
509 options: simple_extensions::EnumOptions(vec!["OVERFLOW".to_string()]),
510 }),
511 simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
512 name: None,
513 description: None,
514 value: text::simple_extensions::Type::String("i32".to_string()),
515 constant: None,
516 }),
517 simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
518 name: None,
519 description: None,
520 type_: "".to_string(),
521 }),
522 ];
523
524 for item in items {
525 let item = item.parse(&mut TestContext)?;
526 let (name, description) = match item {
527 ArgumentsItem::EnumArgument(EnumerationArg {
528 name, description, ..
529 }) => (name, description),
530
531 ArgumentsItem::ValueArgument(ValueArg {
532 name,
533 description,
534 constant,
535 ..
536 }) => {
537 assert!(constant.is_none());
538 (name, description)
539 }
540
541 ArgumentsItem::TypeArgument(TypeArg {
542 name, description, ..
543 }) => (name, description),
544 };
545 assert!(name.is_none());
546 assert!(description.is_none());
547 }
548
549 Ok(())
550 }
551
552 #[test]
553 fn parse_argument_with_empty_fields() -> Result<(), ArgumentsItemError> {
554 let items = vec![
555 simple_extensions::ArgumentsItem::EnumerationArg(simple_extensions::EnumerationArg {
556 name: Some("".to_string()),
557 description: None,
558 options: simple_extensions::EnumOptions(vec!["OVERFLOW".to_string()]),
559 }),
560 simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
561 name: Some("".to_string()),
562 description: None,
563 value: text::simple_extensions::Type::String("i32".to_string()),
564 constant: None,
565 }),
566 simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
567 name: Some("".to_string()),
568 description: None,
569 type_: "".to_string(),
570 }),
571 ];
572 for item in items {
573 assert_eq!(
574 item.parse(&mut TestContext).err(),
575 Some(ArgumentsItemError::EmptyOptionalField("name".to_string()))
576 );
577 }
578
579 let items = vec![
580 simple_extensions::ArgumentsItem::EnumerationArg(simple_extensions::EnumerationArg {
581 name: None,
582 description: Some("".to_string()),
583 options: simple_extensions::EnumOptions(vec!["OVERFLOW".to_string()]),
584 }),
585 simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
586 name: None,
587 description: Some("".to_string()),
588 value: text::simple_extensions::Type::String("i32".to_string()),
589 constant: None,
590 }),
591 simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
592 name: None,
593 description: Some("".to_string()),
594 type_: "".to_string(),
595 }),
596 ];
597 for item in items {
598 assert_eq!(
599 item.parse(&mut TestContext).err(),
600 Some(ArgumentsItemError::EmptyOptionalField(
601 "description".to_string()
602 ))
603 );
604 }
605
606 Ok(())
607 }
608
609 #[test]
610 fn from_enum_argument() {
611 let item: ArgumentsItem = EnumerationArg {
612 name: Some("arg".to_string()),
613 description: Some("desc".to_string()),
614 options: EnumOptions(HashSet::from(["OVERFLOW".to_string()])),
615 }
616 .into();
617
618 let item: text::simple_extensions::ArgumentsItem = item.into();
619 match item {
620 text::simple_extensions::ArgumentsItem::EnumerationArg(
621 simple_extensions::EnumerationArg {
622 name,
623 description,
624 options,
625 },
626 ) => {
627 assert_eq!(name, Some("arg".to_string()));
628 assert_eq!(description, Some("desc".to_string()));
629 assert_eq!(options.0, vec!["OVERFLOW".to_string()]);
630 }
631 _ => unreachable!(),
632 }
633 }
634
635 #[test]
636 fn from_value_argument() {
637 let item: ArgumentsItem = ValueArg {
638 name: Some("arg".to_string()),
639 description: Some("desc".to_string()),
640 value: text::simple_extensions::Type::String("".to_string()),
641 constant: Some(true),
642 }
643 .into();
644
645 let item: text::simple_extensions::ArgumentsItem = item.into();
646 match item {
647 text::simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
648 name,
649 description,
650 value,
651 constant,
652 }) => {
653 assert_eq!(name, Some("arg".to_string()));
654 assert_eq!(description, Some("desc".to_string()));
655 assert!(
656 matches!(value, text::simple_extensions::Type::String(type_) if type_.is_empty())
657 );
658 assert_eq!(constant, Some(true));
659 }
660 _ => unreachable!(),
661 }
662 }
663
664 #[test]
665 fn from_type_argument() {
666 let item: ArgumentsItem = TypeArg {
667 name: Some("arg".to_string()),
668 description: Some("desc".to_string()),
669 type_: "".to_string(),
670 }
671 .into();
672
673 let item: text::simple_extensions::ArgumentsItem = item.into();
674 match item {
675 text::simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
676 name,
677 description,
678 type_,
679 }) => {
680 assert_eq!(name, Some("arg".to_string()));
681 assert_eq!(description, Some("desc".to_string()));
682 assert_eq!(type_, "");
683 }
684 _ => unreachable!(),
685 }
686 }
687
688 #[cfg(feature = "extensions")]
689 #[test]
690 fn parse_extensions() {
691 use crate::extensions::EXTENSIONS;
692
693 macro_rules! parse_arguments {
694 ($url:expr, $fns:expr) => {
695 $fns.iter().for_each(|f| {
696 f.impls
697 .iter()
698 .filter_map(|i| i.args.as_ref())
699 .flat_map(|a| &a.0)
700 .for_each(|item| {
701 item.clone()
702 .parse(&mut TestContext)
703 .unwrap_or_else(|err| {
704 panic!(
705 "found an invalid argument: {}, (url: {}, function: {}, arg: {:?})",
706 err,
707 $url.to_string(),
708 f.name,
709 item
710 );
711 });
712 })
713 });
714 };
715 }
716
717 EXTENSIONS.iter().for_each(|(url, ext)| {
718 parse_arguments!(url, ext.scalar_functions);
719 parse_arguments!(url, ext.aggregate_functions);
720 parse_arguments!(url, ext.window_functions);
721 });
722 }
723}