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