1use std::{
2 borrow::Cow,
3 collections::{BTreeMap, HashMap, HashSet, btree_map::Entry},
4 mem,
5};
6
7use serde::Serialize;
8use unicase::Ascii;
9
10use crate::{
11 Attrs, Expr, Query, Raw, Source, SourceKind, Type, Value, error::AnalysisError, token::Operator,
12};
13
14#[derive(Debug, Clone, Serialize)]
24pub struct Typed {
25 pub project: Type,
30
31 #[serde(skip)]
36 pub scope: Scope,
37}
38
39pub type AnalysisResult<A> = std::result::Result<A, AnalysisError>;
44
45pub struct AnalysisOptions {
51 pub default_scope: Scope,
53 pub event_type_info: Type,
55 pub custom_types: HashSet<Ascii<String>>,
70}
71
72impl AnalysisOptions {
73 pub fn add_custom_type<'a>(mut self, value: impl Into<Cow<'a, str>>) -> Self {
97 match value.into() {
98 Cow::Borrowed(t) => self.custom_types.insert(Ascii::new(t.to_owned())),
99 Cow::Owned(t) => self.custom_types.insert(Ascii::new(t)),
100 };
101
102 self
103 }
104}
105
106impl Default for AnalysisOptions {
107 fn default() -> Self {
108 Self {
109 default_scope: Scope {
110 entries: HashMap::from([
111 (
112 "ABS".to_owned(),
113 Type::App {
114 args: vec![Type::Number],
115 result: Box::new(Type::Number),
116 },
117 ),
118 (
119 "CEIL".to_owned(),
120 Type::App {
121 args: vec![Type::Number],
122 result: Box::new(Type::Number),
123 },
124 ),
125 (
126 "FLOOR".to_owned(),
127 Type::App {
128 args: vec![Type::Number],
129 result: Box::new(Type::Number),
130 },
131 ),
132 (
133 "ROUND".to_owned(),
134 Type::App {
135 args: vec![Type::Number],
136 result: Box::new(Type::Number),
137 },
138 ),
139 (
140 "COS".to_owned(),
141 Type::App {
142 args: vec![Type::Number],
143 result: Box::new(Type::Number),
144 },
145 ),
146 (
147 "EXP".to_owned(),
148 Type::App {
149 args: vec![Type::Number],
150 result: Box::new(Type::Number),
151 },
152 ),
153 (
154 "POW".to_owned(),
155 Type::App {
156 args: vec![Type::Number, Type::Number],
157 result: Box::new(Type::Number),
158 },
159 ),
160 (
161 "SQRT".to_owned(),
162 Type::App {
163 args: vec![Type::Number],
164 result: Box::new(Type::Number),
165 },
166 ),
167 (
168 "RAND".to_owned(),
169 Type::App {
170 args: vec![Type::Number],
171 result: Box::new(Type::Number),
172 },
173 ),
174 (
175 "PI".to_owned(),
176 Type::App {
177 args: vec![Type::Number],
178 result: Box::new(Type::Number),
179 },
180 ),
181 (
182 "LOWER".to_owned(),
183 Type::App {
184 args: vec![Type::String],
185 result: Box::new(Type::String),
186 },
187 ),
188 (
189 "UPPER".to_owned(),
190 Type::App {
191 args: vec![Type::String],
192 result: Box::new(Type::String),
193 },
194 ),
195 (
196 "TRIM".to_owned(),
197 Type::App {
198 args: vec![Type::String],
199 result: Box::new(Type::String),
200 },
201 ),
202 (
203 "LTRIM".to_owned(),
204 Type::App {
205 args: vec![Type::String],
206 result: Box::new(Type::String),
207 },
208 ),
209 (
210 "RTRIM".to_owned(),
211 Type::App {
212 args: vec![Type::String],
213 result: Box::new(Type::String),
214 },
215 ),
216 (
217 "LEN".to_owned(),
218 Type::App {
219 args: vec![Type::String],
220 result: Box::new(Type::Number),
221 },
222 ),
223 (
224 "INSTR".to_owned(),
225 Type::App {
226 args: vec![Type::String],
227 result: Box::new(Type::Number),
228 },
229 ),
230 (
231 "SUBSTRING".to_owned(),
232 Type::App {
233 args: vec![Type::String, Type::Number, Type::Number],
234 result: Box::new(Type::String),
235 },
236 ),
237 (
238 "REPLACE".to_owned(),
239 Type::App {
240 args: vec![Type::String, Type::String, Type::String],
241 result: Box::new(Type::String),
242 },
243 ),
244 (
245 "STARTSWITH".to_owned(),
246 Type::App {
247 args: vec![Type::String, Type::String],
248 result: Box::new(Type::Bool),
249 },
250 ),
251 (
252 "ENDSWITH".to_owned(),
253 Type::App {
254 args: vec![Type::String, Type::String],
255 result: Box::new(Type::Bool),
256 },
257 ),
258 (
259 "NOW".to_owned(),
260 Type::App {
261 args: vec![],
262 result: Box::new(Type::String),
263 },
264 ),
265 (
266 "YEAR".to_owned(),
267 Type::App {
268 args: vec![Type::String],
269 result: Box::new(Type::Number),
270 },
271 ),
272 (
273 "MONTH".to_owned(),
274 Type::App {
275 args: vec![Type::String],
276 result: Box::new(Type::Number),
277 },
278 ),
279 (
280 "DAY".to_owned(),
281 Type::App {
282 args: vec![Type::String],
283 result: Box::new(Type::Number),
284 },
285 ),
286 (
287 "HOUR".to_owned(),
288 Type::App {
289 args: vec![Type::String],
290 result: Box::new(Type::Number),
291 },
292 ),
293 (
294 "MINUTE".to_owned(),
295 Type::App {
296 args: vec![Type::String],
297 result: Box::new(Type::Number),
298 },
299 ),
300 (
301 "SECOND".to_owned(),
302 Type::App {
303 args: vec![Type::String],
304 result: Box::new(Type::Number),
305 },
306 ),
307 (
308 "WEEKDAY".to_owned(),
309 Type::App {
310 args: vec![Type::String],
311 result: Box::new(Type::Number),
312 },
313 ),
314 (
315 "IF".to_owned(),
316 Type::App {
317 args: vec![Type::Bool, Type::Unspecified, Type::Unspecified],
318 result: Box::new(Type::Unspecified),
319 },
320 ),
321 (
322 "COUNT".to_owned(),
323 Type::App {
324 args: vec![],
325 result: Box::new(Type::Number),
326 },
327 ),
328 (
329 "SUM".to_owned(),
330 Type::App {
331 args: vec![Type::Number],
332 result: Box::new(Type::Number),
333 },
334 ),
335 (
336 "AVG".to_owned(),
337 Type::App {
338 args: vec![Type::Number],
339 result: Box::new(Type::Number),
340 },
341 ),
342 (
343 "MIN".to_owned(),
344 Type::App {
345 args: vec![Type::Number],
346 result: Box::new(Type::Number),
347 },
348 ),
349 (
350 "MAX".to_owned(),
351 Type::App {
352 args: vec![Type::Number],
353 result: Box::new(Type::Number),
354 },
355 ),
356 (
357 "MEDIAN".to_owned(),
358 Type::App {
359 args: vec![Type::Number],
360 result: Box::new(Type::Number),
361 },
362 ),
363 (
364 "STDDEV".to_owned(),
365 Type::App {
366 args: vec![Type::Number],
367 result: Box::new(Type::Number),
368 },
369 ),
370 (
371 "VARIANCE".to_owned(),
372 Type::App {
373 args: vec![Type::Number],
374 result: Box::new(Type::Number),
375 },
376 ),
377 (
378 "UNIQUE".to_owned(),
379 Type::App {
380 args: vec![Type::Number],
381 result: Box::new(Type::Number),
382 },
383 ),
384 ]),
385 },
386 event_type_info: Type::Record(BTreeMap::from([
387 ("specversion".to_owned(), Type::String),
388 ("id".to_owned(), Type::String),
389 ("time".to_owned(), Type::String),
390 ("source".to_owned(), Type::String),
391 ("subject".to_owned(), Type::Subject),
392 ("type".to_owned(), Type::String),
393 ("datacontenttype".to_owned(), Type::String),
394 ("data".to_owned(), Type::Unspecified),
395 ("predecessorhash".to_owned(), Type::String),
396 ("hash".to_owned(), Type::String),
397 ("traceparent".to_owned(), Type::String),
398 ("tracestate".to_owned(), Type::String),
399 ("signature".to_owned(), Type::String),
400 ])),
401 custom_types: HashSet::default(),
402 }
403 }
404}
405
406pub fn static_analysis(
424 options: &AnalysisOptions,
425 query: Query<Raw>,
426) -> AnalysisResult<Query<Typed>> {
427 let mut analysis = Analysis::new(options);
428
429 analysis.analyze_query(query)
430}
431
432#[derive(Default, Serialize, Clone, Debug)]
438pub struct Scope {
439 pub entries: HashMap<String, Type>,
441}
442
443impl Scope {
444 pub fn is_empty(&self) -> bool {
446 self.entries.is_empty()
447 }
448}
449
450struct Analysis<'a> {
451 options: &'a AnalysisOptions,
452 prev_scopes: Vec<Scope>,
453 scope: Scope,
454}
455
456impl<'a> Analysis<'a> {
457 fn new(options: &'a AnalysisOptions) -> Self {
458 Self {
459 options,
460 prev_scopes: Default::default(),
461 scope: Scope::default(),
462 }
463 }
464
465 fn enter_scope(&mut self) {
466 if self.scope.is_empty() {
467 return;
468 }
469
470 let prev = mem::take(&mut self.scope);
471 self.prev_scopes.push(prev);
472 }
473
474 fn exit_scope(&mut self) -> Scope {
475 if let Some(prev) = self.prev_scopes.pop() {
476 mem::replace(&mut self.scope, prev)
477 } else {
478 mem::take(&mut self.scope)
479 }
480 }
481
482 fn analyze_query(&mut self, query: Query<Raw>) -> AnalysisResult<Query<Typed>> {
483 self.enter_scope();
484
485 let mut sources = Vec::with_capacity(query.sources.len());
486
487 for source in query.sources {
488 sources.push(self.analyze_source(source)?);
489 }
490
491 if let Some(expr) = &query.predicate {
492 self.analyze_expr(expr, Type::Bool)?;
493 }
494
495 if let Some(group_by) = &query.group_by {
496 if !matches!(&group_by.expr.value, Value::Access(_)) {
497 return Err(AnalysisError::ExpectFieldLiteral(
498 group_by.expr.attrs.pos.line,
499 group_by.expr.attrs.pos.col,
500 ));
501 }
502
503 self.analyze_expr(&group_by.expr, Type::Unspecified)?;
504
505 if let Some(expr) = &group_by.predicate {
506 self.analyze_expr(expr, Type::Bool)?;
507 }
508 }
509
510 if let Some(order_by) = &query.order_by {
511 if !matches!(&order_by.expr.value, Value::Access(_)) {
512 return Err(AnalysisError::ExpectFieldLiteral(
513 order_by.expr.attrs.pos.line,
514 order_by.expr.attrs.pos.col,
515 ));
516 }
517 self.analyze_expr(&order_by.expr, Type::Unspecified)?;
518 }
519
520 if !matches!(&query.projection.value, Value::Record(_) | Value::Id(_)) {
521 return Err(AnalysisError::ExpectRecordLiteral(
522 query.projection.attrs.pos.line,
523 query.projection.attrs.pos.col,
524 ));
525 }
526
527 let project = self.analyze_expr(&query.projection, Type::Unspecified)?;
528
529 if !matches!(&project, Type::Record(f) if !f.is_empty()) {
530 return Err(AnalysisError::ExpectRecord(
531 query.projection.attrs.pos.line,
532 query.projection.attrs.pos.col,
533 project,
534 ));
535 }
536
537 let scope = self.exit_scope();
538
539 Ok(Query {
540 attrs: query.attrs,
541 sources,
542 predicate: query.predicate,
543 group_by: query.group_by,
544 order_by: query.order_by,
545 limit: query.limit,
546 projection: query.projection,
547 distinct: query.distinct,
548 meta: Typed { project, scope },
549 })
550 }
551
552 fn analyze_source(&mut self, source: Source<Raw>) -> AnalysisResult<Source<Typed>> {
553 let kind = self.analyze_source_kind(source.kind)?;
554 let tpe = match &kind {
555 SourceKind::Name(_) | SourceKind::Subject(_) => self.options.event_type_info.clone(),
556 SourceKind::Subquery(query) => self.projection_type(query),
557 };
558
559 if self
560 .scope
561 .entries
562 .insert(source.binding.name.clone(), tpe)
563 .is_some()
564 {
565 return Err(AnalysisError::BindingAlreadyExists(
566 source.binding.pos.line,
567 source.binding.pos.col,
568 source.binding.name,
569 ));
570 }
571
572 Ok(Source {
573 binding: source.binding,
574 kind,
575 })
576 }
577
578 fn analyze_source_kind(&mut self, kind: SourceKind<Raw>) -> AnalysisResult<SourceKind<Typed>> {
579 match kind {
580 SourceKind::Name(n) => Ok(SourceKind::Name(n)),
581 SourceKind::Subject(s) => Ok(SourceKind::Subject(s)),
582 SourceKind::Subquery(query) => {
583 let query = self.analyze_query(*query)?;
584 Ok(SourceKind::Subquery(Box::new(query)))
585 }
586 }
587 }
588
589 fn analyze_expr(&mut self, expr: &Expr, expect: Type) -> AnalysisResult<Type> {
590 self.analyze_value(&expr.attrs, &expr.value, expect)
591 }
592
593 fn analyze_value(
594 &mut self,
595 attrs: &Attrs,
596 value: &Value,
597 mut expect: Type,
598 ) -> AnalysisResult<Type> {
599 match value {
600 Value::Number(_) => expect.check(attrs, Type::Number),
601 Value::String(_) => expect.check(attrs, Type::String),
602 Value::Bool(_) => expect.check(attrs, Type::Bool),
603
604 Value::Id(id) => {
605 if let Some(tpe) = self.options.default_scope.entries.get(id) {
606 expect.check(attrs, tpe.clone())
607 } else if let Some(tpe) = self.scope.entries.get_mut(id.as_str()) {
608 let tmp = mem::take(tpe);
609 *tpe = tmp.check(attrs, expect)?;
610
611 Ok(tpe.clone())
612 } else {
613 Err(AnalysisError::VariableUndeclared(
614 attrs.pos.line,
615 attrs.pos.col,
616 id.to_owned(),
617 ))
618 }
619 }
620
621 Value::Array(exprs) => {
622 if matches!(expect, Type::Unspecified) {
623 for expr in exprs {
624 expect = self.analyze_expr(expr, expect)?;
625 }
626
627 return Ok(Type::Array(Box::new(expect)));
628 }
629
630 match expect {
631 Type::Array(mut expect) => {
632 for expr in exprs {
633 *expect = self.analyze_expr(expr, expect.as_ref().clone())?;
634 }
635
636 Ok(Type::Array(expect))
637 }
638
639 expect => Err(AnalysisError::TypeMismatch(
640 attrs.pos.line,
641 attrs.pos.col,
642 expect,
643 self.project_type(value),
644 )),
645 }
646 }
647
648 Value::Record(fields) => {
649 if matches!(expect, Type::Unspecified) {
650 let mut record = BTreeMap::new();
651
652 for field in fields {
653 record.insert(
654 field.name.clone(),
655 self.analyze_value(
656 &field.value.attrs,
657 &field.value.value,
658 Type::Unspecified,
659 )?,
660 );
661 }
662
663 return Ok(Type::Record(record));
664 }
665
666 match expect {
667 Type::Record(mut types) if fields.len() == types.len() => {
668 for field in fields {
669 if let Some(tpe) = types.remove(field.name.as_str()) {
670 types.insert(
671 field.name.clone(),
672 self.analyze_expr(&field.value, tpe)?,
673 );
674 } else {
675 return Err(AnalysisError::FieldUndeclared(
676 attrs.pos.line,
677 attrs.pos.col,
678 field.name.clone(),
679 ));
680 }
681 }
682
683 Ok(Type::Record(types))
684 }
685
686 expect => Err(AnalysisError::TypeMismatch(
687 attrs.pos.line,
688 attrs.pos.col,
689 expect,
690 self.project_type(value),
691 )),
692 }
693 }
694
695 this @ Value::Access(_) => Ok(self.analyze_access(attrs, this, expect)?),
696
697 this @ Value::App(app) => {
698 if matches!(expect, Type::Unspecified) {
699 return Ok(self.project_type(this));
700 }
701
702 match expect {
703 Type::App { args, mut result } if app.args.len() == args.len() => {
704 let mut arg_types = Vec::with_capacity(args.capacity());
705 for (arg, tpe) in app.args.iter().zip(args.into_iter()) {
706 arg_types.push(self.analyze_expr(arg, tpe)?);
707 }
708
709 if let Some(tpe) = self.options.default_scope.entries.get(app.func.as_str())
710 {
711 let tmp = mem::take(result.as_mut());
712 *result = tmp.check(attrs, tpe.clone())?;
713
714 Ok(Type::App {
715 args: arg_types,
716 result,
717 })
718 } else {
719 Err(AnalysisError::FuncUndeclared(
720 attrs.pos.line,
721 attrs.pos.col,
722 app.func.clone(),
723 ))
724 }
725 }
726
727 expect => Err(AnalysisError::TypeMismatch(
728 attrs.pos.line,
729 attrs.pos.col,
730 expect,
731 self.project_type(value),
732 )),
733 }
734 }
735
736 Value::Binary(binary) => match binary.operator {
737 Operator::Add | Operator::Sub | Operator::Mul | Operator::Div => {
738 self.analyze_expr(&binary.lhs, Type::Number)?;
739 self.analyze_expr(&binary.rhs, Type::Number)?;
740 expect.check(attrs, Type::Number)
741 }
742
743 Operator::Eq
744 | Operator::Neq
745 | Operator::Lt
746 | Operator::Lte
747 | Operator::Gt
748 | Operator::Gte => {
749 let lhs_expect = self.analyze_expr(&binary.lhs, Type::Unspecified)?;
750 let rhs_expect = self.analyze_expr(&binary.rhs, lhs_expect.clone())?;
751
752 if matches!(lhs_expect, Type::Unspecified)
755 && !matches!(rhs_expect, Type::Unspecified)
756 {
757 self.analyze_expr(&binary.lhs, rhs_expect)?;
758 }
759
760 expect.check(attrs, Type::Bool)
761 }
762
763 Operator::Contains => {
764 let lhs_expect =
765 self.analyze_expr(&binary.lhs, Type::Array(Box::new(Type::Unspecified)))?;
766
767 let lhs_assumption = match lhs_expect {
768 Type::Array(inner) => *inner,
769 other => {
770 return Err(AnalysisError::ExpectArray(
771 attrs.pos.line,
772 attrs.pos.col,
773 other,
774 ));
775 }
776 };
777
778 let rhs_expect = self.analyze_expr(&binary.rhs, lhs_assumption.clone())?;
779
780 if matches!(lhs_assumption, Type::Unspecified)
783 && !matches!(rhs_expect, Type::Unspecified)
784 {
785 self.analyze_expr(&binary.lhs, Type::Array(Box::new(rhs_expect)))?;
786 }
787
788 expect.check(attrs, Type::Bool)
789 }
790
791 Operator::And | Operator::Or | Operator::Xor => {
792 self.analyze_expr(&binary.lhs, Type::Bool)?;
793 self.analyze_expr(&binary.rhs, Type::Bool)?;
794
795 expect.check(attrs, Type::Bool)
796 }
797
798 Operator::As => {
799 if let Value::Id(name) = &binary.rhs.value {
800 if let Some(tpe) = name_to_type(self.options, name) {
801 return Ok(tpe);
803 } else {
804 return Err(AnalysisError::UnsupportedCustomType(
805 attrs.pos.line,
806 attrs.pos.col,
807 name.clone(),
808 ));
809 }
810 }
811
812 unreachable!(
813 "we already made sure during parsing that we can only have an ID symbol at this point"
814 )
815 }
816
817 Operator::Not => unreachable!(),
818 },
819
820 Value::Unary(unary) => match unary.operator {
821 Operator::Add | Operator::Sub => {
822 self.analyze_expr(&unary.expr, Type::Number)?;
823 expect.check(attrs, Type::Number)
824 }
825
826 Operator::Not => {
827 self.analyze_expr(&unary.expr, Type::Bool)?;
828 expect.check(attrs, Type::Bool)
829 }
830
831 _ => unreachable!(),
832 },
833
834 Value::Group(expr) => Ok(self.analyze_expr(expr.as_ref(), expect)?),
835 }
836 }
837
838 fn analyze_access(
839 &mut self,
840 attrs: &Attrs,
841 access: &Value,
842 expect: Type,
843 ) -> AnalysisResult<Type> {
844 struct State<A, B> {
845 depth: u8,
846 dynamic: bool,
848 definition: Def<A, B>,
849 }
850
851 impl<A, B> State<A, B> {
852 fn new(definition: Def<A, B>) -> Self {
853 Self {
854 depth: 0,
855 dynamic: false,
856 definition,
857 }
858 }
859 }
860
861 enum Def<A, B> {
862 User(A),
863 System(B),
864 }
865
866 fn go<'a>(
867 scope: &'a mut Scope,
868 sys: &'a AnalysisOptions,
869 attrs: &'a Attrs,
870 value: &'a Value,
871 ) -> AnalysisResult<State<&'a mut Type, &'a Type>> {
872 match value {
873 Value::Id(id) => {
874 if let Some(tpe) = sys.default_scope.entries.get(id.as_str()) {
875 Ok(State::new(Def::System(tpe)))
876 } else if let Some(tpe) = scope.entries.get_mut(id.as_str()) {
877 Ok(State::new(Def::User(tpe)))
878 } else {
879 Err(AnalysisError::VariableUndeclared(
880 attrs.pos.line,
881 attrs.pos.col,
882 id.clone(),
883 ))
884 }
885 }
886 Value::Access(access) => {
887 let mut state = go(scope, sys, &access.target.attrs, &access.target.value)?;
888
889 let is_data_field = state.depth == 0 && access.field == "data";
891
892 if !state.dynamic && is_data_field {
896 state.dynamic = true;
897 }
898
899 match state.definition {
900 Def::User(tpe) => {
901 if matches!(tpe, Type::Unspecified) && state.dynamic {
902 *tpe = Type::Record(BTreeMap::from([(
903 access.field.clone(),
904 Type::Unspecified,
905 )]));
906 return Ok(State {
907 depth: state.depth + 1,
908 definition: Def::User(
909 tpe.as_record_or_panic_mut()
910 .get_mut(access.field.as_str())
911 .unwrap(),
912 ),
913 ..state
914 });
915 }
916
917 if let Type::Record(fields) = tpe {
918 match fields.entry(access.field.clone()) {
919 Entry::Vacant(entry) => {
920 if state.dynamic || is_data_field {
921 return Ok(State {
922 depth: state.depth + 1,
923 definition: Def::User(
924 entry.insert(Type::Unspecified),
925 ),
926 ..state
927 });
928 }
929
930 return Err(AnalysisError::FieldUndeclared(
931 attrs.pos.line,
932 attrs.pos.col,
933 access.field.clone(),
934 ));
935 }
936
937 Entry::Occupied(entry) => {
938 return Ok(State {
939 depth: state.depth + 1,
940 definition: Def::User(entry.into_mut()),
941 ..state
942 });
943 }
944 }
945 }
946
947 Err(AnalysisError::ExpectRecord(
948 attrs.pos.line,
949 attrs.pos.col,
950 tpe.clone(),
951 ))
952 }
953
954 Def::System(tpe) => {
955 if matches!(tpe, Type::Unspecified) && state.dynamic {
956 return Ok(State {
957 depth: state.depth + 1,
958 definition: Def::System(&Type::Unspecified),
959 ..state
960 });
961 }
962
963 if let Type::Record(fields) = tpe {
964 if let Some(field) = fields.get(access.field.as_str()) {
965 return Ok(State {
966 depth: state.depth + 1,
967 definition: Def::System(field),
968 ..state
969 });
970 }
971
972 return Err(AnalysisError::FieldUndeclared(
973 attrs.pos.line,
974 attrs.pos.col,
975 access.field.clone(),
976 ));
977 }
978
979 Err(AnalysisError::ExpectRecord(
980 attrs.pos.line,
981 attrs.pos.col,
982 tpe.clone(),
983 ))
984 }
985 }
986 }
987 Value::Number(_)
988 | Value::String(_)
989 | Value::Bool(_)
990 | Value::Array(_)
991 | Value::Record(_)
992 | Value::App(_)
993 | Value::Binary(_)
994 | Value::Unary(_)
995 | Value::Group(_) => unreachable!(),
996 }
997 }
998
999 let state = go(&mut self.scope, self.options, attrs, access)?;
1000
1001 match state.definition {
1002 Def::User(tpe) => {
1003 let tmp = mem::take(tpe);
1004 *tpe = tmp.check(attrs, expect)?;
1005
1006 Ok(tpe.clone())
1007 }
1008
1009 Def::System(tpe) => tpe.clone().check(attrs, expect),
1010 }
1011 }
1012
1013 fn projection_type(&self, query: &Query<Typed>) -> Type {
1014 self.project_type(&query.projection.value)
1015 }
1016
1017 fn project_type(&self, value: &Value) -> Type {
1018 match value {
1019 Value::Number(_) => Type::Number,
1020 Value::String(_) => Type::String,
1021 Value::Bool(_) => Type::Bool,
1022 Value::Id(id) => {
1023 if let Some(tpe) = self.options.default_scope.entries.get(id) {
1024 tpe.clone()
1025 } else if let Some(tpe) = self.scope.entries.get(id) {
1026 tpe.clone()
1027 } else {
1028 Type::Unspecified
1029 }
1030 }
1031 Value::Array(exprs) => {
1032 let mut project = Type::Unspecified;
1033
1034 for expr in exprs {
1035 let tmp = self.project_type(&expr.value);
1036
1037 if !matches!(tmp, Type::Unspecified) {
1038 project = tmp;
1039 break;
1040 }
1041 }
1042
1043 Type::Array(Box::new(project))
1044 }
1045 Value::Record(fields) => Type::Record(
1046 fields
1047 .iter()
1048 .map(|field| (field.name.clone(), self.project_type(&field.value.value)))
1049 .collect(),
1050 ),
1051 Value::Access(access) => {
1052 let tpe = self.project_type(&access.target.value);
1053 if let Type::Record(fields) = tpe {
1054 fields
1055 .get(access.field.as_str())
1056 .cloned()
1057 .unwrap_or_default()
1058 } else {
1059 Type::Unspecified
1060 }
1061 }
1062 Value::App(app) => self
1063 .options
1064 .default_scope
1065 .entries
1066 .get(app.func.as_str())
1067 .cloned()
1068 .unwrap_or_default(),
1069 Value::Binary(binary) => match binary.operator {
1070 Operator::Add | Operator::Sub | Operator::Mul | Operator::Div => Type::Number,
1071 Operator::As => {
1072 if let Value::Id(n) = &binary.rhs.as_ref().value
1073 && let Some(tpe) = name_to_type(self.options, n.as_str())
1074 {
1075 tpe
1076 } else {
1077 Type::Unspecified
1078 }
1079 }
1080 Operator::Eq
1081 | Operator::Neq
1082 | Operator::Lt
1083 | Operator::Lte
1084 | Operator::Gt
1085 | Operator::Gte
1086 | Operator::And
1087 | Operator::Or
1088 | Operator::Xor
1089 | Operator::Not
1090 | Operator::Contains => Type::Bool,
1091 },
1092 Value::Unary(unary) => match unary.operator {
1093 Operator::Add | Operator::Sub => Type::Number,
1094 Operator::Mul
1095 | Operator::Div
1096 | Operator::Eq
1097 | Operator::Neq
1098 | Operator::Lt
1099 | Operator::Lte
1100 | Operator::Gt
1101 | Operator::Gte
1102 | Operator::And
1103 | Operator::Or
1104 | Operator::Xor
1105 | Operator::Not
1106 | Operator::Contains
1107 | Operator::As => unreachable!(),
1108 },
1109 Value::Group(expr) => self.project_type(&expr.value),
1110 }
1111 }
1112}
1113
1114fn name_to_type(opts: &AnalysisOptions, name: &str) -> Option<Type> {
1115 if name.eq_ignore_ascii_case("string") {
1116 Some(Type::String)
1117 } else if name.eq_ignore_ascii_case("int") || name.eq_ignore_ascii_case("float64") {
1118 Some(Type::Number)
1119 } else if name.eq_ignore_ascii_case("boolean") {
1120 Some(Type::Bool)
1121 } else if name.eq_ignore_ascii_case("date") {
1122 Some(Type::Date)
1123 } else if name.eq_ignore_ascii_case("time") {
1124 Some(Type::Time)
1125 } else if name.eq_ignore_ascii_case("datetime") {
1126 Some(Type::DateTime)
1127 } else if opts.custom_types.contains(&Ascii::new(name.to_owned())) {
1128 Some(Type::Custom(name.to_owned()))
1130 } else {
1131 None
1132 }
1133}