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)]
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)]
234pub struct ValueArg {
235 name: Option<String>,
237
238 description: Option<String>,
240
241 value: simple_extensions::Type,
246
247 constant: Option<bool>,
251}
252
253impl ValueArg {
254 pub fn name(&self) -> Option<&String> {
258 self.name.as_ref()
259 }
260
261 pub fn description(&self) -> Option<&String> {
265 self.description.as_ref()
266 }
267
268 pub fn constant(&self) -> bool {
273 self.constant.unwrap_or(false)
274 }
275}
276
277impl<C: Context> Parse<C> for simple_extensions::ValueArg {
278 type Parsed = ValueArg;
279
280 type Error = ArgumentsItemError;
281
282 fn parse(self, _ctx: &mut C) -> Result<ValueArg, ArgumentsItemError> {
283 Ok(ValueArg {
284 name: ArgumentsItem::parse_name(self.name)?,
285 description: ArgumentsItem::parse_description(self.description)?,
286 value: self.value,
287 constant: self.constant,
288 })
289 }
290}
291
292impl From<ValueArg> for simple_extensions::ValueArg {
293 fn from(value: ValueArg) -> Self {
294 simple_extensions::ValueArg {
295 name: value.name,
296 description: value.description,
297 value: value.value,
298 constant: value.constant,
299 }
300 }
301}
302
303impl From<ValueArg> for simple_extensions::ArgumentsItem {
304 fn from(value: ValueArg) -> Self {
305 simple_extensions::ArgumentsItem::ValueArg(value.into())
306 }
307}
308
309impl From<ValueArg> for ArgumentsItem {
310 fn from(value: ValueArg) -> Self {
311 ArgumentsItem::ValueArgument(value)
312 }
313}
314
315#[derive(Clone, Debug, PartialEq)]
317pub struct TypeArg {
318 name: Option<String>,
320
321 description: Option<String>,
323
324 type_: String,
329}
330
331impl TypeArg {
332 pub fn name(&self) -> Option<&String> {
336 self.name.as_ref()
337 }
338
339 pub fn description(&self) -> Option<&String> {
343 self.description.as_ref()
344 }
345}
346
347impl<C: Context> Parse<C> for simple_extensions::TypeArg {
348 type Parsed = TypeArg;
349
350 type Error = ArgumentsItemError;
351
352 fn parse(self, _ctx: &mut C) -> Result<TypeArg, ArgumentsItemError> {
353 Ok(TypeArg {
354 name: ArgumentsItem::parse_name(self.name)?,
355 description: ArgumentsItem::parse_description(self.description)?,
356 type_: self.type_,
357 })
358 }
359}
360
361impl From<TypeArg> for simple_extensions::TypeArg {
362 fn from(value: TypeArg) -> Self {
363 simple_extensions::TypeArg {
364 name: value.name,
365 description: value.description,
366 type_: value.type_,
367 }
368 }
369}
370
371impl From<TypeArg> for simple_extensions::ArgumentsItem {
372 fn from(value: TypeArg) -> Self {
373 simple_extensions::ArgumentsItem::TypeArg(value.into())
374 }
375}
376
377impl From<TypeArg> for ArgumentsItem {
378 fn from(value: TypeArg) -> Self {
379 ArgumentsItem::TypeArgument(value)
380 }
381}
382
383#[cfg(test)]
384mod tests {
385 use super::*;
386 use crate::text::simple_extensions;
387 use crate::{parse::Context, text};
388
389 pub struct TestContext;
391
392 impl Context for TestContext {}
393
394 #[test]
395 fn parse_enum_argument() -> Result<(), ArgumentsItemError> {
396 let enum_argument =
397 simple_extensions::ArgumentsItem::EnumerationArg(simple_extensions::EnumerationArg {
398 name: Some("arg".to_string()),
399 description: Some("desc".to_string()),
400 options: simple_extensions::EnumOptions(vec!["OVERFLOW".to_string()]),
401 });
402 let item = enum_argument.parse(&mut TestContext)?;
403 let enum_argument = match item {
404 ArgumentsItem::EnumArgument(enum_argument) => enum_argument,
405 _ => unreachable!(),
406 };
407 assert_eq!(
408 enum_argument,
409 EnumerationArg {
410 name: Some("arg".to_string()),
411 description: Some("desc".to_string()),
412 options: EnumOptions(HashSet::from(["OVERFLOW".to_string()])),
413 }
414 );
415 Ok(())
416 }
417
418 #[test]
419 fn parse_empty_enum_options() -> Result<(), ArgumentsItemError> {
420 let options = simple_extensions::EnumOptions(vec![]);
421 let is_err = options
422 .parse(&mut TestContext)
423 .err()
424 .map(|err| matches!(err, EnumOptionsError::EmptyList));
425 assert_eq!(is_err, Some(true));
426 Ok(())
427 }
428
429 #[test]
430 fn parse_enum_options_with_empty_value() -> Result<(), ArgumentsItemError> {
431 let options = simple_extensions::EnumOptions(vec!["".to_string()]);
432 let is_err = options
433 .parse(&mut TestContext)
434 .err()
435 .map(|err| matches!(err, EnumOptionsError::EmptyOption));
436 assert_eq!(is_err, Some(true));
437 Ok(())
438 }
439
440 #[test]
441 fn parse_enum_argument_with_duplicated_option() -> Result<(), ArgumentsItemError> {
442 let options =
443 simple_extensions::EnumOptions(vec!["OVERFLOW".to_string(), "OVERFLOW".to_string()]);
444 let is_err = options.clone().parse(&mut TestContext).err().map(|err| {
445 matches!(
446 err,
447 EnumOptionsError::DuplicatedOption(opt) if opt == "OVERFLOW"
448 )
449 });
450 assert_eq!(is_err, Some(true));
451 Ok(())
452 }
453
454 #[test]
455 fn parse_value_argument() -> Result<(), ArgumentsItemError> {
456 let item = simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
457 name: Some("arg".to_string()),
458 description: Some("desc".to_string()),
459 value: text::simple_extensions::Type::String("i32".to_string()),
460 constant: Some(true),
461 });
462 let item = item.parse(&mut TestContext)?;
463 match item {
464 ArgumentsItem::ValueArgument(ValueArg {
465 name,
466 description,
467 value,
468 constant,
469 }) => {
470 assert_eq!(name, Some("arg".to_string()));
471 assert_eq!(description, Some("desc".to_string()));
472 assert!(
473 matches!(value, text::simple_extensions::Type::String(type_) if type_ == "i32")
474 );
475 assert_eq!(constant, Some(true));
476 }
477 _ => unreachable!(),
478 };
479 Ok(())
480 }
481
482 #[test]
483 fn parse_type_argument() -> Result<(), ArgumentsItemError> {
484 let type_argument = simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
485 name: Some("arg".to_string()),
486 description: Some("desc".to_string()),
487 type_: "".to_string(),
488 });
489 let item = type_argument.parse(&mut TestContext)?;
490 match item {
491 ArgumentsItem::TypeArgument(TypeArg {
492 name,
493 description,
494 type_,
495 }) => {
496 assert_eq!(name, Some("arg".to_string()));
497 assert_eq!(description, Some("desc".to_string()));
498 assert_eq!(type_, "");
499 }
500 _ => unreachable!(),
501 };
502 Ok(())
503 }
504
505 #[test]
506 fn parse_argument_with_nones() -> Result<(), ArgumentsItemError> {
507 let items = vec![
508 simple_extensions::ArgumentsItem::EnumerationArg(simple_extensions::EnumerationArg {
509 name: None,
510 description: None,
511 options: simple_extensions::EnumOptions(vec!["OVERFLOW".to_string()]),
512 }),
513 simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
514 name: None,
515 description: None,
516 value: text::simple_extensions::Type::String("i32".to_string()),
517 constant: None,
518 }),
519 simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
520 name: None,
521 description: None,
522 type_: "".to_string(),
523 }),
524 ];
525
526 for item in items {
527 let item = item.parse(&mut TestContext)?;
528 let (name, description) = match item {
529 ArgumentsItem::EnumArgument(EnumerationArg {
530 name, description, ..
531 }) => (name, description),
532
533 ArgumentsItem::ValueArgument(ValueArg {
534 name,
535 description,
536 constant,
537 ..
538 }) => {
539 assert!(constant.is_none());
540 (name, description)
541 }
542
543 ArgumentsItem::TypeArgument(TypeArg {
544 name, description, ..
545 }) => (name, description),
546 };
547 assert!(name.is_none());
548 assert!(description.is_none());
549 }
550
551 Ok(())
552 }
553
554 #[test]
555 fn parse_argument_with_empty_fields() -> Result<(), ArgumentsItemError> {
556 let items = vec![
557 simple_extensions::ArgumentsItem::EnumerationArg(simple_extensions::EnumerationArg {
558 name: Some("".to_string()),
559 description: None,
560 options: simple_extensions::EnumOptions(vec!["OVERFLOW".to_string()]),
561 }),
562 simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
563 name: Some("".to_string()),
564 description: None,
565 value: text::simple_extensions::Type::String("i32".to_string()),
566 constant: None,
567 }),
568 simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
569 name: Some("".to_string()),
570 description: None,
571 type_: "".to_string(),
572 }),
573 ];
574 for item in items {
575 assert_eq!(
576 item.parse(&mut TestContext).err(),
577 Some(ArgumentsItemError::EmptyOptionalField("name".to_string()))
578 );
579 }
580
581 let items = vec![
582 simple_extensions::ArgumentsItem::EnumerationArg(simple_extensions::EnumerationArg {
583 name: None,
584 description: Some("".to_string()),
585 options: simple_extensions::EnumOptions(vec!["OVERFLOW".to_string()]),
586 }),
587 simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
588 name: None,
589 description: Some("".to_string()),
590 value: text::simple_extensions::Type::String("i32".to_string()),
591 constant: None,
592 }),
593 simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
594 name: None,
595 description: Some("".to_string()),
596 type_: "".to_string(),
597 }),
598 ];
599 for item in items {
600 assert_eq!(
601 item.parse(&mut TestContext).err(),
602 Some(ArgumentsItemError::EmptyOptionalField(
603 "description".to_string()
604 ))
605 );
606 }
607
608 Ok(())
609 }
610
611 #[test]
612 fn from_enum_argument() {
613 let item: ArgumentsItem = EnumerationArg {
614 name: Some("arg".to_string()),
615 description: Some("desc".to_string()),
616 options: EnumOptions(HashSet::from(["OVERFLOW".to_string()])),
617 }
618 .into();
619
620 let item: text::simple_extensions::ArgumentsItem = item.into();
621 match item {
622 text::simple_extensions::ArgumentsItem::EnumerationArg(
623 simple_extensions::EnumerationArg {
624 name,
625 description,
626 options,
627 },
628 ) => {
629 assert_eq!(name, Some("arg".to_string()));
630 assert_eq!(description, Some("desc".to_string()));
631 assert_eq!(options.0, vec!["OVERFLOW".to_string()]);
632 }
633 _ => unreachable!(),
634 }
635 }
636
637 #[test]
638 fn from_value_argument() {
639 let item: ArgumentsItem = ValueArg {
640 name: Some("arg".to_string()),
641 description: Some("desc".to_string()),
642 value: text::simple_extensions::Type::String("".to_string()),
643 constant: Some(true),
644 }
645 .into();
646
647 let item: text::simple_extensions::ArgumentsItem = item.into();
648 match item {
649 text::simple_extensions::ArgumentsItem::ValueArg(simple_extensions::ValueArg {
650 name,
651 description,
652 value,
653 constant,
654 }) => {
655 assert_eq!(name, Some("arg".to_string()));
656 assert_eq!(description, Some("desc".to_string()));
657 assert!(
658 matches!(value, text::simple_extensions::Type::String(type_) if type_.is_empty())
659 );
660 assert_eq!(constant, Some(true));
661 }
662 _ => unreachable!(),
663 }
664 }
665
666 #[test]
667 fn from_type_argument() {
668 let item: ArgumentsItem = TypeArg {
669 name: Some("arg".to_string()),
670 description: Some("desc".to_string()),
671 type_: "".to_string(),
672 }
673 .into();
674
675 let item: text::simple_extensions::ArgumentsItem = item.into();
676 match item {
677 text::simple_extensions::ArgumentsItem::TypeArg(simple_extensions::TypeArg {
678 name,
679 description,
680 type_,
681 }) => {
682 assert_eq!(name, Some("arg".to_string()));
683 assert_eq!(description, Some("desc".to_string()));
684 assert_eq!(type_, "");
685 }
686 _ => unreachable!(),
687 }
688 }
689
690 #[cfg(feature = "extensions")]
691 #[test]
692 fn parse_extensions() {
693 use crate::extensions::EXTENSIONS;
694
695 macro_rules! parse_arguments {
696 ($url:expr, $fns:expr) => {
697 $fns.iter().for_each(|f| {
698 f.impls
699 .iter()
700 .filter_map(|i| i.args.as_ref())
701 .flat_map(|a| &a.0)
702 .for_each(|item| {
703 item.clone()
704 .parse(&mut TestContext)
705 .unwrap_or_else(|err| {
706 panic!(
707 "found an invalid argument: {}, (url: {}, function: {}, arg: {:?})",
708 err,
709 $url.to_string(),
710 f.name,
711 item
712 );
713 });
714 })
715 });
716 };
717 }
718
719 EXTENSIONS.iter().for_each(|(url, ext)| {
720 parse_arguments!(url, ext.scalar_functions);
721 parse_arguments!(url, ext.aggregate_functions);
722 parse_arguments!(url, ext.window_functions);
723 });
724 }
725}