1use crate::data::context::EvalError;
10use crate::data::value::Val;
11use indexmap::IndexMap;
12use std::sync::Arc;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17#[repr(u8)]
18pub enum BuiltinMethod {
19 Len = 0,
22 Keys,
24 Values,
26 Entries,
28 ToPairs,
30 FromPairs,
32 Invert,
34 Reverse,
36 Type,
38 ToString,
40 ToJson,
42 FromJson,
44 Rows,
46
47 Sum,
50 Avg,
52 Min,
54 Max,
56 Count,
58 Any,
60 All,
62 FindIndex,
64 IndicesWhere,
66 MaxBy,
68 MinBy,
70 GroupBy,
72 CountBy,
74 IndexBy,
76 GroupShape,
78 Explode,
80 Implode,
82
83 Filter,
86 Map,
88 FlatMap,
90 Find,
92 FindAll,
94 Sort,
96 Unique,
98 UniqueBy,
100 Collect,
102 DeepFind,
104 DeepShape,
106 DeepLike,
108 Walk,
110 WalkPre,
112 Rec,
114 TracePath,
116 Flatten,
118 Compact,
120 Join,
122 First,
124 Last,
126 Nth,
128 Take,
130 Skip,
132 Append,
134 Prepend,
136 Remove,
138 Diff,
140 Intersect,
142 Union,
144 Enumerate,
146 Pairwise,
148 Window,
150 Chunk,
152 TakeWhile,
154 DropWhile,
156 FindFirst,
158 FindOne,
160 ApproxCountDistinct,
162 Accumulate,
164 Fold,
167 Partition,
169 Zip,
171 ZipLongest,
173 Fanout,
175 ZipShape,
177
178 Pick,
181 Omit,
183 Merge,
185 DeepMerge,
187 Defaults,
189 Rename,
191 TransformKeys,
193 TransformValues,
195 FilterKeys,
197 FilterValues,
199 Pivot,
201
202 GetPath,
205 SetPath,
207 DelPath,
209 DelPaths,
211 HasPath,
213 FlattenKeys,
215 UnflattenKeys,
217
218 ToCsv,
221 ToTsv,
223
224 Or,
227 Has,
229 HasAll,
231 HasKey,
233 Missing,
235 Includes,
237 Index,
239 IndicesOf,
241 Set,
243 Update,
245
246 Ceil,
249 Floor,
251 Round,
253 Abs,
255 RollingSum,
257 RollingAvg,
259 RollingMin,
261 RollingMax,
263 Lag,
265 Lead,
267 DiffWindow,
269 PctChange,
271 CumMax,
273 CumMin,
275 Zscore,
277
278 Upper,
281 Lower,
283 Capitalize,
285 TitleCase,
287 Trim,
289 TrimLeft,
291 TrimRight,
293 SnakeCase,
295 KebabCase,
297 CamelCase,
299 PascalCase,
301 ReverseStr,
303 Lines,
305 Words,
307 Chars,
309 CharsOf,
311 Bytes,
313 ByteLen,
315 IsBlank,
317 IsNumeric,
319 IsAlpha,
321 IsAscii,
323 ToNumber,
325 ToBool,
327 ParseInt,
329 ParseFloat,
331 ParseBool,
333 ToBase64,
335 FromBase64,
337 UrlEncode,
339 UrlDecode,
341 HtmlEscape,
343 HtmlUnescape,
345 Repeat,
347 PadLeft,
349 PadRight,
351 Center,
353 StartsWith,
355 EndsWith,
357 IndexOf,
359 LastIndexOf,
361 Replace,
363 ReplaceAll,
365 StripPrefix,
367 StripSuffix,
369 Slice,
371 Split,
373 Indent,
375 Dedent,
377 Matches,
379 Scan,
381 ReMatch,
383 ReMatchFirst,
385 ReMatchAll,
387 ReCaptures,
389 ReCapturesAll,
391 ReSplit,
393 ReReplace,
395 ReReplaceAll,
397 ContainsAny,
399 ContainsAll,
401 Schema,
403
404 EquiJoin,
407
408 Unknown,
410}
411
412#[macro_export]
416macro_rules! for_each_builtin {
417 ($macro:ident) => {
418 $macro! (
419 Abs, Accumulate, All, Any, Append, ApproxCountDistinct, Avg, ByteLen, Bytes,
420 CamelCase, Capitalize, Ceil, Center, Chars, CharsOf, Chunk, Collect, Compact,
421 ContainsAll, ContainsAny, Count, CountBy, CumMax, CumMin, Dedent, DeepFind,
422 DeepLike, DeepMerge, DeepShape, Defaults, DelPath, DelPaths, Diff, DiffWindow,
423 DropWhile, EndsWith, Entries, Enumerate, EquiJoin, Explode, Fanout, Filter,
424 FilterKeys, FilterValues, Find, FindAll, FindFirst, FindIndex, FindOne, First,
425 FlatMap, Flatten, FlattenKeys, Floor, Fold, FromBase64, FromJson, FromPairs, GetPath,
426 GroupBy, GroupShape, Has, HasAll, HasKey, HasPath, HtmlEscape, HtmlUnescape, Implode,
427 Includes, Indent, Index, IndexBy, IndexOf, IndicesOf, IndicesWhere, Intersect, Invert,
428 IsAlpha, IsAscii, IsBlank, IsNumeric, Join, KebabCase, Keys, Lag, Last,
429 LastIndexOf, Lead, Len, Lines, Lower, Map, Matches, Max, MaxBy, Merge, Min,
430 MinBy, Missing, Nth, Omit, Or, PadLeft, PadRight, Pairwise, ParseBool,
431 ParseFloat, ParseInt, Partition, PascalCase, PctChange, Pick, Pivot, Prepend,
432 Rec, ReCaptures, ReCapturesAll, ReMatch, ReMatchAll, ReMatchFirst, Remove,
433 Rename, Repeat, Replace, ReplaceAll, ReReplace, ReReplaceAll, ReSplit, Reverse,
434 ReverseStr, RollingAvg, RollingMax, RollingMin, RollingSum, Round, Rows, Scan, Schema,
435 Set, SetPath, Skip, Slice, SnakeCase, Sort, Split, StartsWith, StripPrefix,
436 StripSuffix, Sum, Take, TakeWhile, TitleCase, ToBase64, ToBool, ToCsv, ToJson,
437 ToNumber, ToPairs, ToString, ToTsv, TracePath, TransformKeys, TransformValues,
438 Trim, TrimLeft, TrimRight, Type, UnflattenKeys, Union, Unique, UniqueBy, Unknown,
439 Update, Upper, UrlDecode, UrlEncode, Values, Walk, WalkPre, Window, Words, Zip,
440 ZipLongest, ZipShape, Zscore
441 )
442 };
443}
444
445impl BuiltinMethod {
446 pub fn from_name(name: &str) -> Self {
449 crate::builtins::registry::by_name(name)
450 .and_then(|id| id.method())
451 .unwrap_or(Self::Unknown)
452 }
453
454 pub(crate) fn is_lambda_method(self) -> bool {
457 matches!(
458 self,
459 Self::Filter
460 | Self::Map
461 | Self::FlatMap
462 | Self::Sort
463 | Self::Any
464 | Self::All
465 | Self::Count
466 | Self::GroupBy
467 | Self::CountBy
468 | Self::IndexBy
469 | Self::TakeWhile
470 | Self::DropWhile
471 | Self::Accumulate
472 | Self::Fold
473 | Self::Partition
474 | Self::TransformKeys
475 | Self::TransformValues
476 | Self::FilterKeys
477 | Self::FilterValues
478 | Self::Pivot
479 | Self::Update
480 )
481 }
482}
483
484#[derive(Debug, Clone)]
488pub enum BuiltinArgs {
489 None,
491 Str(Arc<str>),
493 Path(Arc<[PathSeg]>),
495 StrPair { first: Arc<str>, second: Arc<str> },
497 StrVec(Vec<Arc<str>>),
499 I64(i64),
501 I64Opt { first: i64, second: Option<i64> },
503 Usize(usize),
505 Val(Val),
507 ValVec(Vec<Val>),
509 Pad { width: usize, fill: char },
511}
512
513#[derive(Debug, Clone)]
516pub struct BuiltinCall {
517 pub method: BuiltinMethod,
519 pub args: BuiltinArgs,
521}
522
523struct StaticArgDecoder<'a, E, I> {
526 name: &'a str,
527 eval_arg: E,
528 ident_arg: I,
529}
530
531impl<E, I> StaticArgDecoder<'_, E, I>
532where
533 E: FnMut(usize) -> Result<Option<Val>, EvalError>,
534 I: FnMut(usize) -> Option<Arc<str>>,
535{
536 fn val(&mut self, idx: usize) -> Result<Val, EvalError> {
538 (self.eval_arg)(idx)?.ok_or_else(|| EvalError(format!("{}: missing argument", self.name)))
539 }
540
541 fn str(&mut self, idx: usize) -> Result<Arc<str>, EvalError> {
543 if let Some(value) = (self.ident_arg)(idx) {
544 return Ok(value);
545 }
546 match self.val(idx)? {
547 Val::Str(s) => Ok(s),
548 other => Ok(Arc::from(crate::util::val_to_string(&other).as_str())),
549 }
550 }
551
552 fn str_lit(&mut self, idx: usize) -> Option<Arc<str>> {
556 match (self.eval_arg)(idx).ok().flatten()? {
557 Val::Str(s) => Some(s),
558 Val::StrSlice(r) => Some(r.to_arc()),
559 _ => None,
560 }
561 }
562
563 fn i64(&mut self, idx: usize) -> Result<i64, EvalError> {
565 match self.val(idx)? {
566 Val::Int(n) => Ok(n),
567 Val::Float(f) => Ok(f as i64),
568 _ => Err(EvalError(format!(
569 "{}: expected number argument",
570 self.name
571 ))),
572 }
573 }
574
575 fn usize(&mut self, idx: usize) -> Result<usize, EvalError> {
577 Ok(self.i64(idx)?.max(0) as usize)
578 }
579
580 fn vec(&mut self, idx: usize) -> Result<Vec<Val>, EvalError> {
582 self.val(idx).and_then(|value| {
583 value
584 .into_vec()
585 .ok_or_else(|| EvalError(format!("{}: expected array arg", self.name)))
586 })
587 }
588
589 fn str_vec(&mut self, idx: usize) -> Result<Vec<Arc<str>>, EvalError> {
591 Ok(self
592 .vec(idx)?
593 .iter()
594 .map(|v| match v {
595 Val::Str(s) => s.clone(),
596 other => Arc::from(crate::util::val_to_string(other).as_str()),
597 })
598 .collect())
599 }
600
601 fn char(&mut self, idx: usize, arg_len: usize) -> Result<char, EvalError> {
604 if idx >= arg_len {
605 return Ok(' ');
606 }
607 match self.str(idx)? {
608 s if s.chars().count() == 1 => Ok(s.chars().next().unwrap()),
609 _ => Err(EvalError(format!(
610 "{}: filler must be a single-char string",
611 self.name
612 ))),
613 }
614 }
615}
616
617#[derive(Debug, Clone, Copy)]
620pub struct BuiltinSpec {
621 pub pure: bool,
623 pub category: BuiltinCategory,
625 pub cardinality: BuiltinCardinality,
627 pub can_indexed: bool,
629 pub view_native: bool,
631 pub view_scalar: bool,
633 pub view_stage: Option<BuiltinViewStage>,
635 pub sink: Option<BuiltinSinkSpec>,
637 pub keyed_reducer: Option<BuiltinKeyedReducer>,
639 pub numeric_reducer: Option<BuiltinNumericReducer>,
641 pub stage_merge: Option<BuiltinStageMerge>,
643 pub cancellation: Option<BuiltinCancellation>,
645 pub columnar_stage: Option<BuiltinColumnarStage>,
647 pub structural: Option<BuiltinStructural>,
649 pub cost: f64,
651 pub demand_law: BuiltinDemandLaw,
653 pub materialization: BuiltinPipelineMaterialization,
655 pub pipeline_shape: Option<BuiltinPipelineShape>,
657 pub order_effect: Option<BuiltinPipelineOrderEffect>,
659 pub lowering: Option<BuiltinPipelineLowering>,
661 pub is_element: bool,
663 pub never_unwrap: bool,
669 pub stream_source: bool,
672}
673
674#[derive(Debug, Clone, Copy, PartialEq, Eq)]
677pub enum BuiltinDemandLaw {
678 Identity,
680 FilterLike,
682 TakeWhile,
684 DropWhile,
686 UniqueLike,
688 MapLike,
690 Slice,
692 FlatMapLike,
694 Take,
696 Skip,
698 Chunk,
700 Window,
702 First,
704 Last,
706 Nth,
708 Count,
710 NumericReducer,
712 KeyOnlyReducer,
714 RowKeyedReducer,
716 OrderBarrier,
718 Reverse,
720}
721
722#[derive(Debug, Clone, Copy, PartialEq, Eq)]
725pub enum BuiltinStructural {
726 DeepFind,
728 DeepShape,
730 DeepLike,
732}
733
734#[derive(Debug, Clone, Copy, PartialEq, Eq)]
737pub enum BuiltinViewStage {
738 Filter,
740 Compact,
742 RemoveValue,
744 Map,
746 FlatMap,
748 TakeWhile,
750 DropWhile,
752 Distinct,
754 KeyedReduce,
756 Take,
758 Skip,
760}
761
762#[derive(Debug, Clone, Copy, PartialEq, Eq)]
764pub enum BuiltinViewInputMode {
765 ReadsView,
767 SkipsViewRead,
769}
770
771#[derive(Debug, Clone, Copy, PartialEq, Eq)]
773pub enum BuiltinViewOutputMode {
774 PreservesInputView,
776 BorrowedSubview,
778 BorrowedSubviews,
780 EmitsOwnedValue,
782}
783
784#[derive(Debug, Clone, Copy, PartialEq, Eq)]
786pub struct BuiltinSinkSpec {
787 pub accumulator: BuiltinSinkAccumulator,
789 pub demand: BuiltinSinkDemand,
791}
792
793#[derive(Debug, Clone, Copy, PartialEq, Eq)]
795pub enum BuiltinSinkAccumulator {
796 Count,
798 Numeric,
800 ApproxDistinct,
802 SelectOne(BuiltinSelectionPosition),
804}
805
806#[derive(Debug, Clone, Copy, PartialEq, Eq)]
808pub enum BuiltinKeyedReducer {
809 Count,
811 Index,
813 Group,
815}
816
817#[derive(Debug, Clone, Copy, PartialEq, Eq)]
819pub enum BuiltinSelectionPosition {
820 First,
822 Last,
824}
825
826#[derive(Debug, Clone, Copy, PartialEq, Eq)]
828pub enum BuiltinSinkDemand {
829 All {
831 value: BuiltinSinkValueNeed,
833 order: bool,
835 },
836 First {
838 value: BuiltinSinkValueNeed,
840 },
841 Last {
843 value: BuiltinSinkValueNeed,
845 },
846}
847
848#[derive(Debug, Clone, Copy, PartialEq, Eq)]
850pub enum BuiltinSinkValueNeed {
851 None,
853 Whole,
855 Numeric,
857}
858
859#[derive(Debug, Clone, Copy, PartialEq, Eq)]
861pub enum BuiltinNumericReducer {
862 Sum,
864 Avg,
866 Min,
868 Max,
870}
871
872#[derive(Debug, Clone, Copy, PartialEq, Eq)]
874pub enum BuiltinStageMerge {
875 UsizeMin,
877 UsizeSaturatingAdd,
879}
880
881#[derive(Debug, Clone, Copy, PartialEq, Eq)]
884pub enum BuiltinCancellation {
885 SelfInverse(BuiltinCancelGroup),
887 Inverse {
889 group: BuiltinCancelGroup,
891 side: BuiltinCancelSide,
893 },
894}
895
896#[derive(Debug, Clone, Copy, PartialEq, Eq)]
898pub enum BuiltinCancelGroup {
899 Reverse,
901 Base64,
903 Url,
905 Html,
907}
908
909#[derive(Debug, Clone, Copy, PartialEq, Eq)]
911pub enum BuiltinCancelSide {
912 Forward,
914 Backward,
916}
917
918impl BuiltinCancellation {
919 #[inline]
921 pub fn cancels_with(self, other: Self) -> bool {
922 match (self, other) {
923 (Self::SelfInverse(a), Self::SelfInverse(b)) => a == b,
924 (Self::Inverse { group: a, side: sa }, Self::Inverse { group: b, side: sb }) => {
925 a == b && sa != sb
926 }
927 _ => false,
928 }
929 }
930}
931
932impl BuiltinStageMerge {
933 #[inline]
935 pub fn combine_usize(self, a: usize, b: usize) -> usize {
936 match self {
937 Self::UsizeMin => a.min(b),
938 Self::UsizeSaturatingAdd => a.saturating_add(b),
939 }
940 }
941}
942
943impl BuiltinViewStage {
944 #[inline]
946 pub fn input_mode(self) -> BuiltinViewInputMode {
947 match self {
948 Self::Filter
949 | Self::Compact
950 | Self::RemoveValue
951 | Self::Map
952 | Self::FlatMap
953 | Self::TakeWhile
954 | Self::DropWhile
955 | Self::Distinct
956 | Self::KeyedReduce => BuiltinViewInputMode::ReadsView,
957 Self::Take | Self::Skip => BuiltinViewInputMode::SkipsViewRead,
958 }
959 }
960
961 #[inline]
963 pub fn output_mode(self) -> BuiltinViewOutputMode {
964 match self {
965 Self::Map => BuiltinViewOutputMode::BorrowedSubview,
966 Self::FlatMap => BuiltinViewOutputMode::BorrowedSubviews,
967 Self::KeyedReduce => BuiltinViewOutputMode::EmitsOwnedValue,
968 Self::Filter
969 | Self::Compact
970 | Self::RemoveValue
971 | Self::TakeWhile
972 | Self::DropWhile
973 | Self::Distinct
974 | Self::Take
975 | Self::Skip => BuiltinViewOutputMode::PreservesInputView,
976 }
977 }
978
979#[inline]
981 pub fn cardinality(self) -> BuiltinCardinality {
982 match self {
983 Self::Filter | Self::Compact | Self::RemoveValue => BuiltinCardinality::Filtering,
984 Self::Map => BuiltinCardinality::OneToOne,
985 Self::FlatMap => BuiltinCardinality::Expanding,
986 Self::TakeWhile | Self::DropWhile => BuiltinCardinality::Filtering,
987 Self::Distinct => BuiltinCardinality::Filtering,
988 Self::KeyedReduce => BuiltinCardinality::Reducing,
989 Self::Take | Self::Skip => BuiltinCardinality::Bounded,
990 }
991 }
992
993 #[inline]
995 pub fn can_indexed(self) -> bool {
996 matches!(self, Self::Map | Self::KeyedReduce)
997 }
998
999 #[inline]
1001 pub fn cost(self) -> f64 {
1002 match self {
1003 Self::Filter
1004 | Self::Compact
1005 | Self::RemoveValue
1006 | Self::Map
1007 | Self::FlatMap
1008 | Self::TakeWhile
1009 | Self::DropWhile
1010 | Self::Distinct
1011 | Self::KeyedReduce => 10.0,
1012 Self::Take | Self::Skip => 0.5,
1013 }
1014 }
1015
1016 #[inline]
1018 pub fn selectivity(self) -> f64 {
1019 match self {
1020 Self::Filter | Self::Compact | Self::RemoveValue | Self::TakeWhile | Self::DropWhile => 0.5,
1021 Self::Distinct => 1.0,
1022 Self::Map | Self::FlatMap | Self::KeyedReduce => 1.0,
1023 Self::Take | Self::Skip => 0.5,
1024 }
1025 }
1026}
1027
1028#[derive(Debug, Clone, Copy, PartialEq)]
1031pub struct BuiltinPipelineShape {
1032 pub cardinality: BuiltinCardinality,
1034 pub can_indexed: bool,
1036 pub cost: f64,
1038 pub selectivity: f64,
1040}
1041
1042#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1044pub enum BuiltinPipelineMaterialization {
1045 Streaming,
1047 ComposedBarrier,
1049 LegacyMaterialized,
1051}
1052
1053#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1055pub enum BuiltinPipelineOrderEffect {
1056 Preserves,
1058 PredicatePrefix,
1060 Blocks,
1062}
1063
1064#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1066pub enum BuiltinColumnarStage {
1067 Filter,
1069 Map,
1071 FlatMap,
1073 GroupBy,
1075}
1076
1077impl BuiltinPipelineShape {
1078 #[inline]
1080 pub fn new(
1081 cardinality: BuiltinCardinality,
1082 can_indexed: bool,
1083 cost: f64,
1084 selectivity: f64,
1085 ) -> Self {
1086 Self {
1087 cardinality,
1088 can_indexed,
1089 cost,
1090 selectivity,
1091 }
1092 }
1093}
1094
1095#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1101pub enum BuiltinPipelineLowering {
1102 ExprArg,
1104 TerminalExprArg {
1106 terminal: BuiltinMethod,
1108 },
1109 Nullary,
1111 UsizeArg {
1113 min: usize,
1115 },
1116 StringArg,
1118 StringPairArg,
1120 IntRangeArg,
1122 Sort,
1124 TerminalSink,
1126 TerminalUsizeSink {
1128 min: usize,
1130 },
1131}
1132
1133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1135pub enum BuiltinCategory {
1136 Scalar,
1138 StreamingOneToOne,
1140 StreamingFilter,
1142 StreamingExpand,
1144 Reducer,
1146 Positional,
1148 Barrier,
1150 Object,
1152 Path,
1154 Deep,
1156 Serialization,
1158 Relational,
1160 Mutation,
1162 Unknown,
1164}
1165
1166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1169pub enum BuiltinCardinality {
1170 OneToOne,
1172 Filtering,
1174 Expanding,
1176 Bounded,
1178 Reducing,
1180 Barrier,
1182}
1183
1184impl BuiltinSpec {
1185 fn new(category: BuiltinCategory, cardinality: BuiltinCardinality) -> Self {
1187 Self {
1188 pure: true,
1189 category,
1190 cardinality,
1191 can_indexed: false,
1192 view_native: false,
1193 view_scalar: false,
1194 view_stage: None,
1195 sink: None,
1196 keyed_reducer: None,
1197 numeric_reducer: None,
1198 stage_merge: None,
1199 cancellation: None,
1200 columnar_stage: None,
1201 structural: None,
1202 cost: 1.0,
1203 demand_law: BuiltinDemandLaw::Identity,
1204 materialization: BuiltinPipelineMaterialization::Streaming,
1205 pipeline_shape: None,
1206 order_effect: None,
1207 lowering: None,
1208 is_element: false,
1209 never_unwrap: false,
1210 stream_source: false,
1211 }
1212 }
1213
1214 pub fn dispatches_scalar_direct(&self) -> bool {
1222 matches!(
1223 self.category,
1224 BuiltinCategory::Scalar | BuiltinCategory::Object
1225 ) && matches!(self.cardinality, BuiltinCardinality::OneToOne)
1226 && !self.never_unwrap
1227 }
1228
1229 fn indexed(mut self) -> Self {
1231 self.can_indexed = true;
1232 self
1233 }
1234
1235 fn view_native(mut self) -> Self {
1237 self.view_native = true;
1238 self
1239 }
1240
1241 fn view_stage(mut self, stage: BuiltinViewStage) -> Self {
1243 self.view_stage = Some(stage);
1244 self
1245 }
1246
1247 fn view_scalar(mut self) -> Self {
1249 self.view_scalar = true;
1250 self.view_native = true;
1251 self
1252 }
1253
1254 fn columnar_stage(mut self, stage: BuiltinColumnarStage) -> Self {
1256 self.columnar_stage = Some(stage);
1257 self
1258 }
1259
1260 fn count_sink(mut self) -> Self {
1262 self.sink = Some(BuiltinSinkSpec {
1263 accumulator: BuiltinSinkAccumulator::Count,
1264 demand: BuiltinSinkDemand::All {
1265 value: BuiltinSinkValueNeed::None,
1266 order: false,
1267 },
1268 });
1269 self
1270 }
1271
1272 fn select_one_sink(mut self, position: BuiltinSelectionPosition) -> Self {
1274 self.sink = Some(BuiltinSinkSpec {
1275 accumulator: BuiltinSinkAccumulator::SelectOne(position),
1276 demand: match position {
1277 BuiltinSelectionPosition::First => BuiltinSinkDemand::First {
1278 value: BuiltinSinkValueNeed::Whole,
1279 },
1280 BuiltinSelectionPosition::Last => BuiltinSinkDemand::Last {
1281 value: BuiltinSinkValueNeed::Whole,
1282 },
1283 },
1284 });
1285 self
1286 }
1287
1288 fn numeric_sink(mut self, reducer: BuiltinNumericReducer) -> Self {
1290 self.sink = Some(BuiltinSinkSpec {
1291 accumulator: BuiltinSinkAccumulator::Numeric,
1292 demand: BuiltinSinkDemand::All {
1293 value: BuiltinSinkValueNeed::Numeric,
1294 order: false,
1295 },
1296 });
1297 self.numeric_reducer = Some(reducer);
1298 self
1299 }
1300
1301 fn approx_distinct_sink(mut self) -> Self {
1303 self.sink = Some(BuiltinSinkSpec {
1304 accumulator: BuiltinSinkAccumulator::ApproxDistinct,
1305 demand: BuiltinSinkDemand::All {
1306 value: BuiltinSinkValueNeed::Whole,
1307 order: false,
1308 },
1309 });
1310 self
1311 }
1312
1313 fn keyed_reducer(mut self, reducer: BuiltinKeyedReducer) -> Self {
1315 self.keyed_reducer = Some(reducer);
1316 self
1317 }
1318
1319 fn stage_merge(mut self, merge: BuiltinStageMerge) -> Self {
1321 self.stage_merge = Some(merge);
1322 self
1323 }
1324
1325 fn cancellation(mut self, cancellation: BuiltinCancellation) -> Self {
1327 self.cancellation = Some(cancellation);
1328 self
1329 }
1330
1331 fn structural(mut self, structural: BuiltinStructural) -> Self {
1333 self.structural = Some(structural);
1334 self
1335 }
1336
1337 fn cost(mut self, cost: f64) -> Self {
1339 self.cost = cost;
1340 self
1341 }
1342
1343 fn demand_law(mut self, law: BuiltinDemandLaw) -> Self {
1345 self.demand_law = law;
1346 self
1347 }
1348
1349 fn materialization(mut self, m: BuiltinPipelineMaterialization) -> Self {
1351 self.materialization = m;
1352 self
1353 }
1354
1355 fn pipeline_shape(mut self, s: BuiltinPipelineShape) -> Self {
1357 self.pipeline_shape = Some(s);
1358 self
1359 }
1360
1361 fn order_effect(mut self, o: BuiltinPipelineOrderEffect) -> Self {
1363 self.order_effect = Some(o);
1364 self
1365 }
1366
1367 fn lowering(mut self, l: BuiltinPipelineLowering) -> Self {
1369 self.lowering = Some(l);
1370 self
1371 }
1372
1373 fn element(mut self) -> Self {
1375 self.is_element = true;
1376 self
1377 }
1378
1379 #[allow(dead_code)]
1384 fn never_unwrap(mut self) -> Self {
1385 self.never_unwrap = true;
1386 self
1387 }
1388
1389 fn stream_source(mut self) -> Self {
1391 self.stream_source = true;
1392 self
1393 }
1394}
1395
1396impl BuiltinMethod {
1397 #[inline]
1400 pub(crate) fn is_string_arg_view_scalar(self) -> bool {
1401 matches!(
1402 self,
1403 Self::StartsWith | Self::EndsWith | Self::Matches | Self::IndexOf | Self::LastIndexOf
1404 )
1405 }
1406
1407 #[inline]
1409 pub(crate) fn is_string_no_arg_view_scalar(self) -> bool {
1410 matches!(
1411 self,
1412 Self::Upper
1413 | Self::Lower
1414 | Self::Trim
1415 | Self::TrimLeft
1416 | Self::TrimRight
1417 | Self::ByteLen
1418 | Self::IsBlank
1419 | Self::IsNumeric
1420 | Self::IsAlpha
1421 | Self::IsAscii
1422 | Self::ToNumber
1423 | Self::ToBool
1424 )
1425 }
1426
1427 #[inline]
1429 pub(crate) fn is_numeric_no_arg_view_scalar(self) -> bool {
1430 matches!(self, Self::Ceil | Self::Floor | Self::Round | Self::Abs)
1431 }
1432
1433 #[inline]
1435 pub(crate) fn is_view_scalar_method(self) -> bool {
1436 self == Self::Len
1437 || self.is_string_arg_view_scalar()
1438 || self.is_string_no_arg_view_scalar()
1439 || self.is_numeric_no_arg_view_scalar()
1440 }
1441
1442 #[inline]
1445 pub(crate) fn is_view_object_key_method(self) -> bool {
1446 matches!(
1447 self,
1448 Self::Has | Self::HasKey | Self::Missing | Self::GetPath | Self::HasPath
1449 )
1450 }
1451
1452 #[inline]
1455 pub(crate) fn is_view_projection_method(self) -> bool {
1456 self.spec().view_scalar || self.is_view_object_key_method()
1457 }
1458
1459 #[inline]
1462 pub fn spec(self) -> BuiltinSpec {
1463 macro_rules! spec_arm {
1464 ( $( $variant:ident ),* $(,)? ) => {
1465 match self {
1466 $( Self::$variant => <defs::$variant as builtin::Builtin>::spec(), )*
1467 }
1468 };
1469 }
1470 let spec = crate::for_each_builtin!(spec_arm);
1471 macro_rules! cancel_arm {
1473 ( $( $variant:ident ),* $(,)? ) => {
1474 match self {
1475 $( Self::$variant => <defs::$variant as builtin::Builtin>::cancellation(), )*
1476 }
1477 };
1478 }
1479 match crate::for_each_builtin!(cancel_arm) {
1480 Some(c) => spec.cancellation(c),
1481 None => spec,
1482 }
1483 }
1484}
1485
1486impl BuiltinCall {
1487 #[inline]
1489 pub fn new(method: BuiltinMethod, args: BuiltinArgs) -> Self {
1490 Self { method, args }
1491 }
1492
1493 #[inline]
1496 pub fn spec(&self) -> BuiltinSpec {
1497 let mut spec = self.method.spec();
1498 let (cost, can_indexed) = match self.method {
1499 BuiltinMethod::Keys | BuiltinMethod::Values | BuiltinMethod::Entries => (1.0, false),
1500 BuiltinMethod::Repeat
1501 | BuiltinMethod::Indent
1502 | BuiltinMethod::PadLeft
1503 | BuiltinMethod::PadRight
1504 | BuiltinMethod::Center => (2.0, true),
1505 BuiltinMethod::IndexOf
1506 | BuiltinMethod::LastIndexOf
1507 | BuiltinMethod::Scan
1508 | BuiltinMethod::StartsWith
1509 | BuiltinMethod::EndsWith
1510 | BuiltinMethod::StripPrefix
1511 | BuiltinMethod::StripSuffix
1512 | BuiltinMethod::Matches
1513 | BuiltinMethod::ReMatch
1514 | BuiltinMethod::ReMatchFirst
1515 | BuiltinMethod::ReMatchAll
1516 | BuiltinMethod::ReCaptures
1517 | BuiltinMethod::ReCapturesAll
1518 | BuiltinMethod::ReSplit
1519 | BuiltinMethod::ReReplace
1520 | BuiltinMethod::ReReplaceAll
1521 | BuiltinMethod::ContainsAny
1522 | BuiltinMethod::ContainsAll => (2.0, true),
1523 _ => (spec.cost, spec.can_indexed),
1524 };
1525 spec.cost = cost;
1526 spec.can_indexed = can_indexed;
1527 spec
1528 }
1529
1530 #[inline]
1533 pub fn is_idempotent(&self) -> bool {
1534 matches!(
1535 self.method,
1536 BuiltinMethod::Upper
1537 | BuiltinMethod::Lower
1538 | BuiltinMethod::Trim
1539 | BuiltinMethod::TrimLeft
1540 | BuiltinMethod::TrimRight
1541 | BuiltinMethod::Capitalize
1542 | BuiltinMethod::TitleCase
1543 | BuiltinMethod::SnakeCase
1544 | BuiltinMethod::KebabCase
1545 | BuiltinMethod::CamelCase
1546 | BuiltinMethod::PascalCase
1547 | BuiltinMethod::Dedent
1548 )
1549 }
1550
1551 pub fn apply(&self, recv: &Val) -> Option<Val> {
1555 macro_rules! apply_or_recv {
1556 ($expr:expr) => {
1557 return Some($expr.unwrap_or_else(|| recv.clone()))
1558 };
1559 }
1560 macro_rules! trait_arm {
1563 ( $( $variant:ident ),* $(,)? ) => {
1564 match self.method {
1565 $( BuiltinMethod::$variant => {
1566 if matches!(self.args, BuiltinArgs::None) {
1567 if let Some(v) = <defs::$variant as builtin::Builtin>::apply_one(recv) {
1568 return Some(v);
1569 }
1570 }
1571 if let Some(v) = <defs::$variant as builtin::Builtin>::apply_args(recv, &self.args) {
1572 return Some(v);
1573 }
1574 } )*
1575 }
1576 };
1577 }
1578 crate::for_each_builtin!(trait_arm);
1579 match (self.method, &self.args) {
1580 (BuiltinMethod::ByteLen, BuiltinArgs::None)
1581 | (BuiltinMethod::IsBlank, BuiltinArgs::None)
1582 | (BuiltinMethod::IsNumeric, BuiltinArgs::None)
1583 | (BuiltinMethod::IsAlpha, BuiltinArgs::None)
1584 | (BuiltinMethod::IsAscii, BuiltinArgs::None)
1585 | (BuiltinMethod::ToNumber, BuiltinArgs::None)
1586 | (BuiltinMethod::ToBool, BuiltinArgs::None) => {
1587 apply_or_recv!(str_no_arg_scalar_val_apply(self.method, recv))
1588 }
1589 (BuiltinMethod::Sum, BuiltinArgs::None)
1590 | (BuiltinMethod::Avg, BuiltinArgs::None)
1591 | (BuiltinMethod::Min, BuiltinArgs::None)
1592 | (BuiltinMethod::Max, BuiltinArgs::None) => {
1593 return Some(numeric_aggregate_apply(recv, self.method));
1594 }
1595 (BuiltinMethod::Len, BuiltinArgs::None) | (BuiltinMethod::Count, BuiltinArgs::None) => {
1596 apply_or_recv!(len_apply(recv))
1597 }
1598 (BuiltinMethod::Keys, BuiltinArgs::None) => return Some(keys_apply(recv)),
1599 (BuiltinMethod::Values, BuiltinArgs::None) => return Some(values_apply(recv)),
1600 (BuiltinMethod::Entries, BuiltinArgs::None) => return Some(entries_apply(recv)),
1601 (BuiltinMethod::Collect, BuiltinArgs::None) => return Some(collect_apply(recv)),
1602 (BuiltinMethod::FromJson, BuiltinArgs::None) => return from_json_apply(recv),
1603 (BuiltinMethod::Ceil, BuiltinArgs::None)
1604 | (BuiltinMethod::Floor, BuiltinArgs::None)
1605 | (BuiltinMethod::Round, BuiltinArgs::None)
1606 | (BuiltinMethod::Abs, BuiltinArgs::None) => {
1607 return numeric_no_arg_scalar_val_apply(self.method, recv)
1608 }
1609 (BuiltinMethod::Or, BuiltinArgs::Val(default)) => return Some(or_apply(recv, default)),
1610 (BuiltinMethod::Missing, BuiltinArgs::Str(k)) => return Some(missing_apply(recv, k)),
1611 (BuiltinMethod::Includes, BuiltinArgs::Val(item)) => {
1612 return Some(includes_apply(recv, item))
1613 }
1614 (BuiltinMethod::Index, BuiltinArgs::Val(item)) => return index_value_apply(recv, item),
1615 (BuiltinMethod::IndicesOf, BuiltinArgs::Val(item)) => {
1616 return indices_of_apply(recv, item)
1617 }
1618 (BuiltinMethod::Set, BuiltinArgs::Val(item)) => return Some(item.clone()),
1619 (BuiltinMethod::Join, BuiltinArgs::Str(sep)) => return join_apply(recv, sep),
1620 (BuiltinMethod::Enumerate, BuiltinArgs::None) => return enumerate_apply(recv),
1621 (BuiltinMethod::Flatten, BuiltinArgs::Usize(depth)) => {
1622 apply_or_recv!(flatten_depth_apply(recv, *depth))
1623 }
1624 (BuiltinMethod::First, BuiltinArgs::I64(n)) => apply_or_recv!(first_apply(recv, *n)),
1625 (BuiltinMethod::Last, BuiltinArgs::I64(n)) => apply_or_recv!(last_apply(recv, *n)),
1626 (BuiltinMethod::Nth, BuiltinArgs::I64(n)) => apply_or_recv!(nth_any_apply(recv, *n)),
1627 (BuiltinMethod::Append, BuiltinArgs::Val(item)) => {
1628 apply_or_recv!(append_apply(recv, item))
1629 }
1630 (BuiltinMethod::Prepend, BuiltinArgs::Val(item)) => {
1631 apply_or_recv!(prepend_apply(recv, item))
1632 }
1633 (BuiltinMethod::Remove, BuiltinArgs::Val(item)) => {
1634 apply_or_recv!(remove_value_apply(recv, item))
1635 }
1636 (BuiltinMethod::Diff, BuiltinArgs::ValVec(other)) => {
1637 let arr_recv = recv.clone().into_vec().map(Val::arr)?;
1638 apply_or_recv!(diff_apply(&arr_recv, other))
1639 }
1640 (BuiltinMethod::Intersect, BuiltinArgs::ValVec(other)) => {
1641 let arr_recv = recv.clone().into_vec().map(Val::arr)?;
1642 apply_or_recv!(intersect_apply(&arr_recv, other))
1643 }
1644 (BuiltinMethod::Union, BuiltinArgs::ValVec(other)) => {
1645 let arr_recv = recv.clone().into_vec().map(Val::arr)?;
1646 apply_or_recv!(union_apply(&arr_recv, other))
1647 }
1648 (BuiltinMethod::Window, BuiltinArgs::Usize(n)) => {
1649 let arr_recv = recv.clone().into_vec().map(Val::arr)?;
1650 apply_or_recv!(window_arr_apply(&arr_recv, *n))
1651 }
1652 (BuiltinMethod::Chunk, BuiltinArgs::Usize(n)) => {
1653 let arr_recv = recv.clone().into_vec().map(Val::arr)?;
1654 apply_or_recv!(chunk_arr_apply(&arr_recv, *n))
1655 }
1656 (BuiltinMethod::RollingSum, BuiltinArgs::Usize(n)) => {
1657 apply_or_recv!(rolling_sum_apply(recv, *n))
1658 }
1659 (BuiltinMethod::RollingAvg, BuiltinArgs::Usize(n)) => {
1660 apply_or_recv!(rolling_avg_apply(recv, *n))
1661 }
1662 (BuiltinMethod::RollingMin, BuiltinArgs::Usize(n)) => {
1663 apply_or_recv!(rolling_min_apply(recv, *n))
1664 }
1665 (BuiltinMethod::RollingMax, BuiltinArgs::Usize(n)) => {
1666 apply_or_recv!(rolling_max_apply(recv, *n))
1667 }
1668 (BuiltinMethod::Lag, BuiltinArgs::Usize(n)) => apply_or_recv!(lag_apply(recv, *n)),
1669 (BuiltinMethod::Lead, BuiltinArgs::Usize(n)) => apply_or_recv!(lead_apply(recv, *n)),
1670 (BuiltinMethod::Merge, BuiltinArgs::Val(other)) => {
1671 apply_or_recv!(merge_apply(recv, other))
1672 }
1673 (BuiltinMethod::DeepMerge, BuiltinArgs::Val(other)) => {
1674 apply_or_recv!(deep_merge_apply(recv, other))
1675 }
1676 (BuiltinMethod::Defaults, BuiltinArgs::Val(other)) => {
1677 apply_or_recv!(defaults_apply(recv, other))
1678 }
1679 (BuiltinMethod::Rename, BuiltinArgs::Val(other)) => {
1680 apply_or_recv!(rename_apply(recv, other))
1681 }
1682 (BuiltinMethod::Explode, BuiltinArgs::Str(field)) => {
1683 apply_or_recv!(explode_apply(recv, field))
1684 }
1685 (BuiltinMethod::Implode, BuiltinArgs::Str(field)) => {
1686 apply_or_recv!(implode_apply(recv, field))
1687 }
1688 (BuiltinMethod::Has, BuiltinArgs::Str(k)) => {
1689 apply_or_recv!(has_apply(recv, k))
1690 }
1691 (BuiltinMethod::HasAll, BuiltinArgs::Val(v)) => {
1692 apply_or_recv!(has_all_apply(recv, v))
1693 }
1694 (BuiltinMethod::HasAll, BuiltinArgs::StrVec(keys)) => {
1695 apply_or_recv!(has_all_keys_apply(recv, keys))
1696 }
1697 (BuiltinMethod::HasKey, BuiltinArgs::Str(k)) => return Some(has_key_apply(recv, k)),
1698 (BuiltinMethod::GetPath, BuiltinArgs::Str(p)) => {
1699 apply_or_recv!(get_path_apply(recv, p))
1700 }
1701 (BuiltinMethod::GetPath, BuiltinArgs::Path(path)) => {
1702 return Some(get_path_impl(recv, path))
1703 }
1704 (BuiltinMethod::HasPath, BuiltinArgs::Str(p)) => {
1705 apply_or_recv!(has_path_apply(recv, p))
1706 }
1707 (BuiltinMethod::HasPath, BuiltinArgs::Path(path)) => {
1708 return Some(Val::Bool(!get_path_impl(recv, path).is_null()))
1709 }
1710 (BuiltinMethod::DelPath, BuiltinArgs::Str(p)) => {
1711 apply_or_recv!(del_path_apply(recv, p))
1712 }
1713 (BuiltinMethod::FlattenKeys, BuiltinArgs::Str(p)) => {
1714 apply_or_recv!(flatten_keys_apply(recv, p))
1715 }
1716 (BuiltinMethod::UnflattenKeys, BuiltinArgs::Str(p)) => {
1717 apply_or_recv!(unflatten_keys_apply(recv, p))
1718 }
1719 (BuiltinMethod::StartsWith, BuiltinArgs::Str(p))
1720 | (BuiltinMethod::EndsWith, BuiltinArgs::Str(p))
1721 | (BuiltinMethod::Matches, BuiltinArgs::Str(p))
1722 | (BuiltinMethod::IndexOf, BuiltinArgs::Str(p))
1723 | (BuiltinMethod::LastIndexOf, BuiltinArgs::Str(p)) => {
1724 apply_or_recv!(str_arg_scalar_val_apply(self.method, recv, p))
1725 }
1726 (BuiltinMethod::StripPrefix, BuiltinArgs::Str(p)) => {
1727 apply_or_recv!(strip_prefix_apply(recv, p))
1728 }
1729 (BuiltinMethod::StripSuffix, BuiltinArgs::Str(p)) => {
1730 apply_or_recv!(strip_suffix_apply(recv, p))
1731 }
1732 (BuiltinMethod::Scan, BuiltinArgs::Str(p)) => apply_or_recv!(scan_apply(recv, p)),
1733 (BuiltinMethod::Split, BuiltinArgs::Str(p)) => apply_or_recv!(split_apply(recv, p)),
1734 (BuiltinMethod::Slice, BuiltinArgs::I64Opt { first, second }) => {
1735 return Some(slice_apply(recv.clone(), *first, *second));
1736 }
1737 (BuiltinMethod::Replace, BuiltinArgs::StrPair { first, second }) => {
1738 apply_or_recv!(replace_apply(recv.clone(), first, second, false))
1739 }
1740 (BuiltinMethod::ReplaceAll, BuiltinArgs::StrPair { first, second }) => {
1741 apply_or_recv!(replace_apply(recv.clone(), first, second, true))
1742 }
1743 (BuiltinMethod::ReMatch, BuiltinArgs::Str(p)) => {
1744 apply_or_recv!(re_match_apply(recv, p))
1745 }
1746 (BuiltinMethod::ReMatchFirst, BuiltinArgs::Str(p)) => {
1747 apply_or_recv!(re_match_first_apply(recv, p))
1748 }
1749 (BuiltinMethod::ReMatchAll, BuiltinArgs::Str(p)) => {
1750 apply_or_recv!(re_match_all_apply(recv, p))
1751 }
1752 (BuiltinMethod::ReCaptures, BuiltinArgs::Str(p)) => {
1753 apply_or_recv!(re_captures_apply(recv, p))
1754 }
1755 (BuiltinMethod::ReCapturesAll, BuiltinArgs::Str(p)) => {
1756 apply_or_recv!(re_captures_all_apply(recv, p))
1757 }
1758 (BuiltinMethod::ReSplit, BuiltinArgs::Str(p)) => {
1759 apply_or_recv!(re_split_apply(recv, p))
1760 }
1761 (BuiltinMethod::ReReplace, BuiltinArgs::StrPair { first, second }) => {
1762 apply_or_recv!(re_replace_apply(recv, first, second))
1763 }
1764 (BuiltinMethod::ReReplaceAll, BuiltinArgs::StrPair { first, second }) => {
1765 apply_or_recv!(re_replace_all_apply(recv, first, second))
1766 }
1767 (BuiltinMethod::ContainsAny, BuiltinArgs::StrVec(ns)) => {
1768 apply_or_recv!(contains_any_apply(recv, ns))
1769 }
1770 (BuiltinMethod::ContainsAll, BuiltinArgs::StrVec(ns)) => {
1771 apply_or_recv!(contains_all_apply(recv, ns))
1772 }
1773 (BuiltinMethod::Pick, BuiltinArgs::StrVec(keys)) => {
1774 apply_or_recv!(pick_apply(recv, keys))
1775 }
1776 (BuiltinMethod::Omit, BuiltinArgs::StrVec(keys)) => {
1777 apply_or_recv!(omit_apply(recv, keys))
1778 }
1779 (BuiltinMethod::Repeat, BuiltinArgs::Usize(n)) => {
1780 apply_or_recv!(repeat_apply(recv, *n))
1781 }
1782 (BuiltinMethod::Indent, BuiltinArgs::Usize(n)) => {
1783 apply_or_recv!(indent_apply(recv, *n))
1784 }
1785 (BuiltinMethod::Indent, BuiltinArgs::Str(prefix)) => {
1786 apply_or_recv!(indent_with_prefix_apply(recv, prefix.as_ref()))
1787 }
1788 (BuiltinMethod::PadLeft, BuiltinArgs::Pad { width, fill }) => {
1789 apply_or_recv!(pad_left_apply(recv, *width, *fill))
1790 }
1791 (BuiltinMethod::PadRight, BuiltinArgs::Pad { width, fill }) => {
1792 apply_or_recv!(pad_right_apply(recv, *width, *fill))
1793 }
1794 (BuiltinMethod::Center, BuiltinArgs::Pad { width, fill }) => {
1795 apply_or_recv!(center_apply(recv, *width, *fill))
1796 }
1797 _ => None,
1798 }
1799 }
1800
1801 pub fn try_apply(&self, recv: &Val) -> Result<Option<Val>, EvalError> {
1804 match (self.method, &self.args) {
1805 (BuiltinMethod::ReMatch, BuiltinArgs::Str(p)) => try_re_match_apply(recv, p),
1806 (BuiltinMethod::ReMatchFirst, BuiltinArgs::Str(p)) => try_re_match_first_apply(recv, p),
1807 (BuiltinMethod::ReMatchAll, BuiltinArgs::Str(p)) => try_re_match_all_apply(recv, p),
1808 (BuiltinMethod::ReCaptures, BuiltinArgs::Str(p)) => try_re_captures_apply(recv, p),
1809 (BuiltinMethod::ReCapturesAll, BuiltinArgs::Str(p)) => {
1810 try_re_captures_all_apply(recv, p)
1811 }
1812 (BuiltinMethod::ReSplit, BuiltinArgs::Str(p)) => try_re_split_apply(recv, p),
1813 (BuiltinMethod::ReReplace, BuiltinArgs::StrPair { first, second }) => {
1814 try_re_replace_apply(recv, first, second)
1815 }
1816 (BuiltinMethod::ReReplaceAll, BuiltinArgs::StrPair { first, second }) => {
1817 try_re_replace_all_apply(recv, first, second)
1818 }
1819 (BuiltinMethod::FromJson, BuiltinArgs::None) => try_from_json_apply(recv),
1820 (BuiltinMethod::Join, BuiltinArgs::Str(sep)) => join_apply(recv, sep)
1821 .map(Some)
1822 .ok_or_else(|| EvalError("join: expected array".into())),
1823 (BuiltinMethod::Enumerate, BuiltinArgs::None) => enumerate_apply(recv)
1824 .map(Some)
1825 .ok_or_else(|| EvalError("enumerate: expected array".into())),
1826 (BuiltinMethod::Sort, BuiltinArgs::None) => sort_apply(recv.clone()).map(Some),
1827 (BuiltinMethod::Index, BuiltinArgs::Val(item)) => index_value_apply(recv, item)
1828 .map(Some)
1829 .ok_or_else(|| EvalError("index: expected array".into())),
1830 (BuiltinMethod::IndicesOf, BuiltinArgs::Val(item)) => indices_of_apply(recv, item)
1831 .map(Some)
1832 .ok_or_else(|| EvalError("indices_of: expected array".into())),
1833 (BuiltinMethod::Ceil, BuiltinArgs::None) => try_ceil_apply(recv),
1834 (BuiltinMethod::Floor, BuiltinArgs::None) => try_floor_apply(recv),
1835 (BuiltinMethod::Round, BuiltinArgs::None) => try_round_apply(recv),
1836 (BuiltinMethod::Abs, BuiltinArgs::None) => try_abs_apply(recv),
1837 (BuiltinMethod::RollingSum, BuiltinArgs::Usize(0)) => {
1838 Err(EvalError("rolling_sum: window must be > 0".into()))
1839 }
1840 (BuiltinMethod::RollingAvg, BuiltinArgs::Usize(0)) => {
1841 Err(EvalError("rolling_avg: window must be > 0".into()))
1842 }
1843 (BuiltinMethod::RollingMin, BuiltinArgs::Usize(0)) => {
1844 Err(EvalError("rolling_min: window must be > 0".into()))
1845 }
1846 (BuiltinMethod::RollingMax, BuiltinArgs::Usize(0)) => {
1847 Err(EvalError("rolling_max: window must be > 0".into()))
1848 }
1849 (BuiltinMethod::RollingSum, BuiltinArgs::Usize(_))
1850 | (BuiltinMethod::RollingAvg, BuiltinArgs::Usize(_))
1851 | (BuiltinMethod::RollingMin, BuiltinArgs::Usize(_))
1852 | (BuiltinMethod::RollingMax, BuiltinArgs::Usize(_))
1853 | (BuiltinMethod::Lag, BuiltinArgs::Usize(_))
1854 | (BuiltinMethod::Lead, BuiltinArgs::Usize(_))
1855 | (BuiltinMethod::DiffWindow, BuiltinArgs::None)
1856 | (BuiltinMethod::PctChange, BuiltinArgs::None)
1857 | (BuiltinMethod::CumMax, BuiltinArgs::None)
1858 | (BuiltinMethod::CumMin, BuiltinArgs::None)
1859 | (BuiltinMethod::Zscore, BuiltinArgs::None) => self
1860 .apply(recv)
1861 .map(Some)
1862 .ok_or_else(|| EvalError("expected numeric array".into())),
1863 _ => Ok(self.apply(recv)),
1864 }
1865 }
1866
1867 pub fn from_static_args<E, I>(
1872 method: BuiltinMethod,
1873 name: &str,
1874 arg_len: usize,
1875 eval_arg: E,
1876 ident_arg: I,
1877 ) -> Result<Option<Self>, EvalError>
1878 where
1879 E: FnMut(usize) -> Result<Option<Val>, EvalError>,
1880 I: FnMut(usize) -> Option<Arc<str>>,
1881 {
1882 if method == BuiltinMethod::Unknown {
1883 return Ok(None);
1884 }
1885
1886 let mut args = StaticArgDecoder {
1887 name,
1888 eval_arg,
1889 ident_arg,
1890 };
1891
1892 let call = match method {
1893 BuiltinMethod::Flatten => {
1894 let depth = if arg_len > 0 { args.usize(0)? } else { 1 };
1895 Self::new(method, BuiltinArgs::Usize(depth))
1896 }
1897 BuiltinMethod::First | BuiltinMethod::Last => {
1898 let n = if arg_len > 0 { args.i64(0)? } else { 1 };
1899 Self::new(method, BuiltinArgs::I64(n))
1900 }
1901 BuiltinMethod::Nth => Self::new(method, BuiltinArgs::I64(args.i64(0)?)),
1902 BuiltinMethod::Take | BuiltinMethod::Skip => {
1903 Self::new(method, BuiltinArgs::Usize(args.usize(0)?))
1904 }
1905 BuiltinMethod::Append | BuiltinMethod::Prepend | BuiltinMethod::Set => {
1906 let item = if arg_len > 0 { args.val(0)? } else { Val::Null };
1907 Self::new(method, BuiltinArgs::Val(item))
1908 }
1909 BuiltinMethod::Or => {
1910 let default = if arg_len > 0 { args.val(0)? } else { Val::Null };
1911 Self::new(method, BuiltinArgs::Val(default))
1912 }
1913 BuiltinMethod::Includes | BuiltinMethod::Index | BuiltinMethod::IndicesOf => {
1914 Self::new(method, BuiltinArgs::Val(args.val(0)?))
1915 }
1916 BuiltinMethod::Diff | BuiltinMethod::Intersect | BuiltinMethod::Union => {
1917 Self::new(method, BuiltinArgs::ValVec(args.vec(0)?))
1918 }
1919 BuiltinMethod::Window
1920 | BuiltinMethod::Chunk
1921 | BuiltinMethod::RollingSum
1922 | BuiltinMethod::RollingAvg
1923 | BuiltinMethod::RollingMin
1924 | BuiltinMethod::RollingMax => Self::new(method, BuiltinArgs::Usize(args.usize(0)?)),
1925 BuiltinMethod::Lag | BuiltinMethod::Lead => {
1926 let n = if arg_len > 0 { args.usize(0)? } else { 1 };
1927 Self::new(method, BuiltinArgs::Usize(n))
1928 }
1929 BuiltinMethod::Merge
1930 | BuiltinMethod::DeepMerge
1931 | BuiltinMethod::Defaults
1932 | BuiltinMethod::Rename => Self::new(method, BuiltinArgs::Val(args.val(0)?)),
1933 BuiltinMethod::Slice => {
1934 let start = args.i64(0)?;
1935 let end = if arg_len > 1 {
1936 Some(args.i64(1)?)
1937 } else {
1938 None
1939 };
1940 Self::new(
1941 method,
1942 BuiltinArgs::I64Opt {
1943 first: start,
1944 second: end,
1945 },
1946 )
1947 }
1948 BuiltinMethod::Missing if arg_len >= 2 => {
1952 let mut keys = Vec::with_capacity(arg_len);
1953 for i in 0..arg_len {
1954 keys.push(args.str(i)?);
1955 }
1956 Self::new(method, BuiltinArgs::StrVec(keys))
1957 }
1958 BuiltinMethod::GetPath | BuiltinMethod::HasPath => {
1959 let path = args.str(0)?;
1960 Self::new(method, BuiltinArgs::Path(parse_path_segs(path.as_ref()).into()))
1961 }
1962 BuiltinMethod::HasAll => Self::new(method, BuiltinArgs::Val(args.val(0)?)),
1963 BuiltinMethod::Has
1964 | BuiltinMethod::HasKey
1965 | BuiltinMethod::Join
1966 | BuiltinMethod::Explode
1967 | BuiltinMethod::Implode
1968 | BuiltinMethod::DelPath
1969 | BuiltinMethod::FlattenKeys
1970 | BuiltinMethod::UnflattenKeys
1971 | BuiltinMethod::Missing
1972 | BuiltinMethod::StartsWith
1973 | BuiltinMethod::EndsWith
1974 | BuiltinMethod::IndexOf
1975 | BuiltinMethod::LastIndexOf
1976 | BuiltinMethod::StripPrefix
1977 | BuiltinMethod::StripSuffix
1978 | BuiltinMethod::Matches
1979 | BuiltinMethod::Scan
1980 | BuiltinMethod::Split
1981 | BuiltinMethod::ReMatch
1982 | BuiltinMethod::ReMatchFirst
1983 | BuiltinMethod::ReMatchAll
1984 | BuiltinMethod::ReCaptures
1985 | BuiltinMethod::ReCapturesAll
1986 | BuiltinMethod::ReSplit => {
1987 let s = if arg_len > 0 {
1988 args.str(0)?
1989 } else if matches!(method, BuiltinMethod::Join) {
1990 Arc::from("")
1991 } else if matches!(
1992 method,
1993 BuiltinMethod::FlattenKeys | BuiltinMethod::UnflattenKeys
1994 ) {
1995 Arc::from(".")
1996 } else {
1997 return Ok(None);
1998 };
1999 Self::new(method, BuiltinArgs::Str(s))
2000 }
2001 BuiltinMethod::Replace
2002 | BuiltinMethod::ReplaceAll
2003 | BuiltinMethod::ReReplace
2004 | BuiltinMethod::ReReplaceAll => Self::new(
2005 method,
2006 BuiltinArgs::StrPair {
2007 first: args.str(0)?,
2008 second: args.str(1)?,
2009 },
2010 ),
2011 BuiltinMethod::ContainsAny | BuiltinMethod::ContainsAll => {
2012 Self::new(method, BuiltinArgs::StrVec(args.str_vec(0)?))
2013 }
2014 BuiltinMethod::Omit => {
2015 let mut keys = Vec::with_capacity(arg_len);
2016 for idx in 0..arg_len {
2017 keys.push(args.str(idx)?);
2018 }
2019 Self::new(method, BuiltinArgs::StrVec(keys))
2020 }
2021 BuiltinMethod::Repeat => Self::new(method, BuiltinArgs::Usize(args.usize(0)?)),
2022 BuiltinMethod::Indent => {
2023 if arg_len > 0 {
2024 if let Some(prefix) = args.str_lit(0) {
2025 Self::new(method, BuiltinArgs::Str(prefix))
2026 } else {
2027 Self::new(method, BuiltinArgs::Usize(args.usize(0)?))
2028 }
2029 } else {
2030 Self::new(method, BuiltinArgs::Usize(2))
2031 }
2032 }
2033 BuiltinMethod::PadLeft | BuiltinMethod::PadRight | BuiltinMethod::Center => Self::new(
2034 method,
2035 BuiltinArgs::Pad {
2036 width: args.usize(0)?,
2037 fill: args.char(1, arg_len)?,
2038 },
2039 ),
2040 _ if arg_len == 0 => Self::new(method, BuiltinArgs::None),
2041 _ => return Ok(None),
2042 };
2043 Ok(Some(call))
2044 }
2045
2046 pub fn from_literal_ast_args(name: &str, args: &[crate::parse::ast::Arg]) -> Option<Self> {
2050 use crate::parse::ast::{Arg, ArrayElem, Expr, ObjField};
2051
2052 let method = BuiltinMethod::from_name(name);
2053 if method == BuiltinMethod::Unknown {
2054 return None;
2055 }
2056
2057 fn literal_val(expr: &Expr) -> Option<Val> {
2058 match expr {
2059 Expr::Null => Some(Val::Null),
2060 Expr::Bool(b) => Some(Val::Bool(*b)),
2061 Expr::Int(n) => Some(Val::Int(*n)),
2062 Expr::Float(f) => Some(Val::Float(*f)),
2063 Expr::Str(s) => Some(Val::Str(Arc::from(s.as_str()))),
2064 Expr::Array(elems) => {
2065 let mut out = Vec::with_capacity(elems.len());
2066 for elem in elems {
2067 match elem {
2068 ArrayElem::Expr(expr) => out.push(literal_val(expr)?),
2069 ArrayElem::Spread(_) => return None,
2070 }
2071 }
2072 Some(Val::Arr(Arc::new(out)))
2073 }
2074 Expr::Object(fields) => {
2075 let mut out = IndexMap::with_capacity(fields.len());
2076 for field in fields {
2077 match field {
2078 ObjField::Kv {
2079 key,
2080 val,
2081 optional: false,
2082 cond: None,
2083 } => {
2084 out.insert(Arc::from(key.as_str()), literal_val(val)?);
2085 }
2086 _ => return None,
2087 }
2088 }
2089 Some(Val::Obj(Arc::new(out)))
2090 }
2091 _ => None,
2092 }
2093 }
2094
2095 if method == BuiltinMethod::Remove {
2096 return match args {
2097 [Arg::Pos(expr)] => {
2098 Some(Self::new(method, BuiltinArgs::Val(literal_val(expr)?)))
2099 }
2100 _ => None,
2101 };
2102 }
2103
2104 if method == BuiltinMethod::HasAll {
2105 return match args {
2106 [Arg::Pos(Expr::Array(elems))] => {
2107 let mut keys = Vec::with_capacity(elems.len());
2108 for elem in elems {
2109 let ArrayElem::Expr(expr) = elem else {
2110 return None;
2111 };
2112 keys.push(Arc::from(crate::util::val_to_key(&literal_val(expr)?)));
2113 }
2114 Some(Self::new(method, BuiltinArgs::StrVec(keys)))
2115 }
2116 [Arg::Pos(expr)] => Some(Self::new(method, BuiltinArgs::Val(literal_val(expr)?))),
2117 _ => None,
2118 };
2119 }
2120
2121 Self::from_static_args(
2122 method,
2123 name,
2124 args.len(),
2125 |idx| {
2126 Ok(match args.get(idx) {
2127 Some(Arg::Pos(expr)) => literal_val(expr),
2128 _ => None,
2129 })
2130 },
2131 |idx| match args.get(idx) {
2132 Some(Arg::Pos(Expr::Ident(value))) => Some(Arc::from(value.as_str())),
2133 _ => None,
2134 },
2135 )
2136 .ok()
2137 .flatten()
2138 }
2139
2140 pub fn from_pipeline_literal_args(name: &str, args: &[crate::parse::ast::Arg]) -> Option<Self> {
2143 let call = Self::from_literal_ast_args(name, args)?;
2144 call.method.is_pipeline_element_method().then_some(call)
2145 }
2146
2147 pub fn try_apply_json_view(&self, recv: crate::util::JsonView<'_>) -> Option<Val> {
2150 if !self.spec().view_scalar {
2151 return None;
2152 }
2153 match (self.method, &self.args) {
2154 (BuiltinMethod::Len, BuiltinArgs::None) => json_view_len(recv).map(Val::Int),
2155 (method, BuiltinArgs::None) if method.is_string_no_arg_view_scalar() => {
2156 let value = json_view_str(recv)?;
2157 str_no_arg_scalar_apply(method, value)
2158 }
2159 (method, BuiltinArgs::None) if method.is_numeric_no_arg_view_scalar() => {
2160 numeric_no_arg_scalar_apply(method, recv)
2161 }
2162 (method, BuiltinArgs::Str(arg)) if method.is_string_arg_view_scalar() => {
2163 let value = json_view_str(recv)?;
2164 str_arg_scalar_apply(method, value, arg.as_ref())
2165 }
2166 (BuiltinMethod::Includes, BuiltinArgs::Val(Val::Str(arg))) => {
2167 let value = json_view_str(recv)?;
2168 Some(Val::Bool(value.contains(arg.as_ref())))
2169 }
2170 (BuiltinMethod::Includes, BuiltinArgs::Val(Val::StrSlice(arg))) => {
2171 let value = json_view_str(recv)?;
2172 Some(Val::Bool(value.contains(arg.as_str())))
2173 }
2174 _ => None,
2175 }
2176 }
2177}
2178
2179#[inline]
2181fn numeric_no_arg_scalar_apply(
2182 method: BuiltinMethod,
2183 recv: crate::util::JsonView<'_>,
2184) -> Option<Val> {
2185 match (method, recv) {
2186 (
2187 BuiltinMethod::Ceil | BuiltinMethod::Floor | BuiltinMethod::Round,
2188 crate::util::JsonView::Int(n),
2189 ) => Some(Val::Int(n)),
2190 (
2191 BuiltinMethod::Ceil | BuiltinMethod::Floor | BuiltinMethod::Round,
2192 crate::util::JsonView::UInt(n),
2193 ) => Some(uint_to_val(n)),
2194 (BuiltinMethod::Ceil, crate::util::JsonView::Float(f)) => Some(Val::Int(f.ceil() as i64)),
2195 (BuiltinMethod::Floor, crate::util::JsonView::Float(f)) => Some(Val::Int(f.floor() as i64)),
2196 (BuiltinMethod::Round, crate::util::JsonView::Float(f)) => Some(Val::Int(f.round() as i64)),
2197 (BuiltinMethod::Abs, crate::util::JsonView::Int(n)) => Some(Val::Int(n.wrapping_abs())),
2198 (BuiltinMethod::Abs, crate::util::JsonView::UInt(n)) => Some(uint_to_val(n)),
2199 (BuiltinMethod::Abs, crate::util::JsonView::Float(f)) => Some(Val::Float(f.abs())),
2200 _ => None,
2201 }
2202}
2203
2204#[inline]
2206fn numeric_no_arg_scalar_val_apply(method: BuiltinMethod, recv: &Val) -> Option<Val> {
2207 numeric_no_arg_scalar_apply(method, crate::util::JsonView::from_val(recv))
2208}
2209
2210#[inline]
2212fn uint_to_val(n: u64) -> Val {
2213 if n <= i64::MAX as u64 {
2214 Val::Int(n as i64)
2215 } else {
2216 Val::Float(n as f64)
2217 }
2218}
2219
2220#[inline]
2222fn str_no_arg_scalar_apply(method: BuiltinMethod, value: &str) -> Option<Val> {
2223 match method {
2224 BuiltinMethod::Upper => {
2225 if value.is_ascii() {
2226 let mut buf = value.to_owned();
2227 buf.make_ascii_uppercase();
2228 Some(Val::Str(Arc::from(buf)))
2229 } else {
2230 Some(Val::Str(Arc::from(value.to_uppercase())))
2231 }
2232 }
2233 BuiltinMethod::Lower => {
2234 if value.is_ascii() {
2235 let mut buf = value.to_owned();
2236 buf.make_ascii_lowercase();
2237 Some(Val::Str(Arc::from(buf)))
2238 } else {
2239 Some(Val::Str(Arc::from(value.to_lowercase())))
2240 }
2241 }
2242 BuiltinMethod::Trim => Some(Val::Str(Arc::from(value.trim()))),
2243 BuiltinMethod::TrimLeft => Some(Val::Str(Arc::from(value.trim_start()))),
2244 BuiltinMethod::TrimRight => Some(Val::Str(Arc::from(value.trim_end()))),
2245 BuiltinMethod::ByteLen => Some(Val::Int(value.len() as i64)),
2246 BuiltinMethod::IsBlank => Some(Val::Bool(value.chars().all(|c| c.is_whitespace()))),
2247 BuiltinMethod::IsNumeric => Some(Val::Bool(
2248 !value.is_empty() && value.chars().all(|c| c.is_ascii_digit()),
2249 )),
2250 BuiltinMethod::IsAlpha => Some(Val::Bool(
2251 !value.is_empty() && value.chars().all(|c| c.is_alphabetic()),
2252 )),
2253 BuiltinMethod::IsAscii => Some(Val::Bool(value.is_ascii())),
2254 BuiltinMethod::ToNumber => {
2255 if let Ok(i) = value.parse::<i64>() {
2256 return Some(Val::Int(i));
2257 }
2258 if let Ok(f) = value.parse::<f64>() {
2259 return Some(Val::Float(f));
2260 }
2261 Some(Val::Null)
2262 }
2263 BuiltinMethod::ToBool => Some(match value {
2264 "true" => Val::Bool(true),
2265 "false" => Val::Bool(false),
2266 _ => Val::Null,
2267 }),
2268 _ => None,
2269 }
2270}
2271
2272#[inline]
2274fn str_no_arg_scalar_val_apply(method: BuiltinMethod, recv: &Val) -> Option<Val> {
2275 str_no_arg_scalar_apply(method, recv.as_str_ref()?)
2276}
2277
2278#[inline]
2280fn str_arg_scalar_apply(method: BuiltinMethod, value: &str, arg: &str) -> Option<Val> {
2281 match method {
2282 BuiltinMethod::StartsWith => Some(Val::Bool(value.starts_with(arg))),
2283 BuiltinMethod::EndsWith => Some(Val::Bool(value.ends_with(arg))),
2284 BuiltinMethod::Matches => Some(Val::Bool(value.contains(arg))),
2285 BuiltinMethod::IndexOf => Some(str_index_of(value, arg, false)),
2286 BuiltinMethod::LastIndexOf => Some(str_index_of(value, arg, true)),
2287 _ => None,
2288 }
2289}
2290
2291#[inline]
2293fn str_arg_scalar_val_apply(method: BuiltinMethod, recv: &Val, arg: &str) -> Option<Val> {
2294 str_arg_scalar_apply(method, recv.as_str_ref()?, arg)
2295}
2296
2297#[inline]
2300fn str_index_of(value: &str, needle: &str, last: bool) -> Val {
2301 let offset = if last {
2302 value.rfind(needle)
2303 } else {
2304 value.find(needle)
2305 };
2306 match offset {
2307 Some(i) => Val::Int(value[..i].chars().count() as i64),
2308 None => Val::Int(-1),
2309 }
2310}
2311
2312#[inline]
2314fn json_view_len(recv: crate::util::JsonView<'_>) -> Option<i64> {
2315 match recv {
2316 crate::util::JsonView::Str(s) => Some(s.chars().count() as i64),
2317 crate::util::JsonView::ArrayLen(n) | crate::util::JsonView::ObjectLen(n) => Some(n as i64),
2318 _ => None,
2319 }
2320}
2321
2322#[inline]
2324fn json_view_str(recv: crate::util::JsonView<'_>) -> Option<&str> {
2325 match recv {
2326 crate::util::JsonView::Str(s) => Some(s),
2327 _ => None,
2328 }
2329}
2330
2331pub(crate) fn eval_builtin_method<F, G, H>(
2340 recv: Val,
2341 name: &str,
2342 args: &[crate::parse::ast::Arg],
2343 mut eval_arg: F,
2344 mut eval_item: G,
2345 mut eval_pair: H,
2346) -> Result<Val, EvalError>
2347where
2348 F: FnMut(&crate::parse::ast::Arg) -> Result<Val, EvalError>,
2349 G: FnMut(&Val, &crate::parse::ast::Arg) -> Result<Val, EvalError>,
2350 H: FnMut(&Val, &Val, &crate::parse::ast::Arg) -> Result<Val, EvalError>,
2351{
2352 use crate::parse::ast::{Arg, Expr, ObjField};
2353
2354 let method = BuiltinMethod::from_name(name);
2355 if method == BuiltinMethod::Unknown {
2356 return Err(EvalError(format!("unknown method '{}'", name)));
2357 }
2358
2359 macro_rules! arg_val {
2360 ($idx:expr) => {{
2361 let arg = args
2362 .get($idx)
2363 .ok_or_else(|| EvalError(format!("{}: missing argument", name)))?;
2364 eval_arg(arg)
2365 }};
2366 }
2367
2368 macro_rules! str_arg {
2369 ($idx:expr) => {{
2370 match args.get($idx) {
2371 Some(Arg::Pos(Expr::Ident(s))) => Ok(Arc::from(s.as_str())),
2372 Some(_) => match arg_val!($idx)? {
2373 Val::Str(s) => Ok(s),
2374 other => Ok(Arc::from(crate::util::val_to_string(&other).as_str())),
2375 },
2376 None => Err(EvalError(format!("{}: missing argument", name))),
2377 }
2378 }};
2379 }
2380
2381 macro_rules! i64_arg {
2382 ($idx:expr) => {{
2383 match arg_val!($idx)? {
2384 Val::Int(n) => Ok(n),
2385 Val::Float(f) => Ok(f as i64),
2386 _ => Err(EvalError(format!("{}: expected number argument", name))),
2387 }
2388 }};
2389 }
2390
2391 macro_rules! vec_arg {
2392 ($idx:expr) => {{
2393 arg_val!($idx)?
2394 .into_vec()
2395 .ok_or_else(|| EvalError(format!("{}: expected array arg", name)))
2396 }};
2397 }
2398
2399 macro_rules! str_vec_arg {
2400 ($idx:expr) => {{
2401 Ok(vec_arg!($idx)?
2402 .iter()
2403 .map(|v| match v {
2404 Val::Str(s) => s.clone(),
2405 other => Arc::from(crate::util::val_to_string(other).as_str()),
2406 })
2407 .collect())
2408 }};
2409 }
2410
2411 macro_rules! fill_arg {
2412 ($idx:expr) => {{
2413 match args.get($idx) {
2414 None => Ok(' '),
2415 Some(_) => {
2416 let s = str_arg!($idx)?;
2417 if s.chars().count() == 1 {
2418 Ok(s.chars().next().unwrap())
2419 } else {
2420 Err(EvalError(format!(
2421 "{}: filler must be a single-char string",
2422 name
2423 )))
2424 }
2425 }
2426 }
2427 }};
2428 }
2429
2430 let call = match method {
2431 BuiltinMethod::Len
2432 | BuiltinMethod::Count
2433 | BuiltinMethod::Sum
2434 | BuiltinMethod::Avg
2435 | BuiltinMethod::Min
2436 | BuiltinMethod::Max
2437 | BuiltinMethod::Keys
2438 | BuiltinMethod::Values
2439 | BuiltinMethod::Entries
2440 | BuiltinMethod::Reverse
2441 | BuiltinMethod::Unique
2442 | BuiltinMethod::Collect
2443 | BuiltinMethod::Compact
2444 | BuiltinMethod::FromJson
2445 | BuiltinMethod::FromPairs
2446 | BuiltinMethod::ToPairs
2447 | BuiltinMethod::Invert
2448 | BuiltinMethod::Enumerate
2449 | BuiltinMethod::Pairwise
2450 | BuiltinMethod::Ceil
2451 | BuiltinMethod::Floor
2452 | BuiltinMethod::Round
2453 | BuiltinMethod::Abs
2454 | BuiltinMethod::DiffWindow
2455 | BuiltinMethod::PctChange
2456 | BuiltinMethod::CumMax
2457 | BuiltinMethod::CumMin
2458 | BuiltinMethod::Zscore
2459 | BuiltinMethod::Upper
2460 | BuiltinMethod::Lower
2461 | BuiltinMethod::Trim
2462 | BuiltinMethod::TrimLeft
2463 | BuiltinMethod::TrimRight
2464 | BuiltinMethod::Capitalize
2465 | BuiltinMethod::TitleCase
2466 | BuiltinMethod::SnakeCase
2467 | BuiltinMethod::KebabCase
2468 | BuiltinMethod::CamelCase
2469 | BuiltinMethod::PascalCase
2470 | BuiltinMethod::ReverseStr
2471 | BuiltinMethod::HtmlEscape
2472 | BuiltinMethod::HtmlUnescape
2473 | BuiltinMethod::UrlEncode
2474 | BuiltinMethod::UrlDecode
2475 | BuiltinMethod::ToBase64
2476 | BuiltinMethod::FromBase64
2477 | BuiltinMethod::Dedent
2478 | BuiltinMethod::Lines
2479 | BuiltinMethod::Words
2480 | BuiltinMethod::Chars
2481 | BuiltinMethod::CharsOf
2482 | BuiltinMethod::Bytes
2483 | BuiltinMethod::ByteLen
2484 | BuiltinMethod::IsBlank
2485 | BuiltinMethod::IsNumeric
2486 | BuiltinMethod::IsAlpha
2487 | BuiltinMethod::IsAscii
2488 | BuiltinMethod::ToNumber
2489 | BuiltinMethod::ToBool
2490 | BuiltinMethod::ParseInt
2491 | BuiltinMethod::ParseFloat
2492 | BuiltinMethod::ParseBool
2493 | BuiltinMethod::Type
2494 | BuiltinMethod::ToString
2495 | BuiltinMethod::ToJson
2496 | BuiltinMethod::ToCsv
2497 | BuiltinMethod::ToTsv
2498 | BuiltinMethod::Schema
2499 | BuiltinMethod::ApproxCountDistinct
2500 | BuiltinMethod::ZipShape
2501 | BuiltinMethod::GroupShape
2502 if args.is_empty() =>
2503 {
2504 BuiltinCall::new(method, BuiltinArgs::None)
2505 }
2506 BuiltinMethod::Sum | BuiltinMethod::Avg | BuiltinMethod::Min | BuiltinMethod::Max => {
2507 return numeric_aggregate_projected_apply(&recv, method, |item| {
2508 eval_item(item, &args[0])
2509 });
2510 }
2511 BuiltinMethod::Count => {
2512 let items = recv
2513 .as_vals()
2514 .ok_or_else(|| EvalError("count: expected array".into()))?;
2515 let mut n: i64 = 0;
2516 for item in items.iter() {
2517 if crate::util::is_truthy(&eval_item(item, &args[0])?) {
2518 n += 1;
2519 }
2520 }
2521 return Ok(Val::Int(n));
2522 }
2523 BuiltinMethod::Find | BuiltinMethod::FindFirst => {
2524 return find_first_apply(recv, args.len(), |item, idx| eval_item(item, &args[idx]));
2525 }
2526 BuiltinMethod::FindAll => {
2527 return find_apply(recv, args.len(), |item, idx| eval_item(item, &args[idx]));
2528 }
2529 BuiltinMethod::FindIndex => {
2530 return find_index_apply(recv, args.len(), |item, idx| eval_item(item, &args[idx]));
2531 }
2532 BuiltinMethod::IndicesWhere => {
2533 return indices_where_apply(recv, args.len(), |item, idx| eval_item(item, &args[idx]));
2534 }
2535 BuiltinMethod::UniqueBy => {
2536 let key_arg = args
2537 .first()
2538 .ok_or_else(|| EvalError("unique_by: requires key fn".into()))?;
2539 return unique_by_apply(recv, |item| eval_item(item, key_arg));
2540 }
2541 BuiltinMethod::MaxBy | BuiltinMethod::MinBy => {
2542 let key_arg = args
2543 .first()
2544 .ok_or_else(|| EvalError(format!("{}: requires a key expression", name)))?;
2545 return extreme_by_apply(recv, method == BuiltinMethod::MaxBy, |item| {
2546 eval_item(item, key_arg)
2547 });
2548 }
2549 BuiltinMethod::DeepFind => {
2550 return deep_find_apply(recv, args.len(), |item, idx| eval_item(item, &args[idx]));
2551 }
2552 BuiltinMethod::DeepShape => {
2553 let arg = args
2554 .first()
2555 .ok_or_else(|| EvalError("shape: requires pattern".into()))?;
2556 let expr = match arg {
2557 Arg::Pos(e) | Arg::Named(_, e) => e,
2558 };
2559 let Expr::Object(fields) = expr else {
2560 return Err(EvalError(
2561 "shape: expected `{k1, k2, ...}` object pattern".into(),
2562 ));
2563 };
2564 let mut keys = Vec::with_capacity(fields.len());
2565 for field in fields {
2566 match field {
2567 ObjField::Short(k) => keys.push(Arc::from(k.as_str())),
2568 ObjField::Kv { key, val, .. } if matches!(val, Expr::Ident(n) if n == key) => {
2569 keys.push(Arc::from(key.as_str()));
2570 }
2571 _ => return Err(EvalError("shape: unsupported pattern field".into())),
2572 }
2573 }
2574 return deep_shape_apply(recv, &keys);
2575 }
2576 BuiltinMethod::DeepLike => {
2577 let arg = args
2578 .first()
2579 .ok_or_else(|| EvalError("like: requires pattern".into()))?;
2580 let expr = match arg {
2581 Arg::Pos(e) | Arg::Named(_, e) => e,
2582 };
2583 let Expr::Object(fields) = expr else {
2584 return Err(EvalError(
2585 "like: expected `{k: lit, ...}` object pattern".into(),
2586 ));
2587 };
2588 let mut pats = Vec::with_capacity(fields.len());
2589 for field in fields {
2590 match field {
2591 ObjField::Kv { key, val, .. } => {
2592 pats.push((Arc::from(key.as_str()), eval_arg(&Arg::Pos(val.clone()))?));
2593 }
2594 ObjField::Short(k) => {
2595 pats.push((
2596 Arc::from(k.as_str()),
2597 eval_arg(&Arg::Pos(Expr::Ident(k.clone())))?,
2598 ));
2599 }
2600 _ => return Err(EvalError("like: unsupported pattern field".into())),
2601 }
2602 }
2603 return deep_like_apply(recv, &pats);
2604 }
2605 BuiltinMethod::Walk | BuiltinMethod::WalkPre => {
2606 let arg = args
2607 .first()
2608 .ok_or_else(|| EvalError("walk: requires fn".into()))?;
2609 let pre = method == BuiltinMethod::WalkPre;
2610 let mut eval = |value: Val| eval_item(&value, arg);
2611 return walk_apply(recv, pre, &mut eval);
2612 }
2613 BuiltinMethod::Rec => {
2614 let arg = args
2615 .first()
2616 .ok_or_else(|| EvalError("rec: requires step expression".into()))?;
2617 if let Some(cond_arg) = args.get(1) {
2618 let eval_cell = std::cell::RefCell::new(eval_item);
2619 return rec_cond_apply(
2620 recv,
2621 |value| eval_cell.borrow_mut()(&value, arg),
2622 |value| eval_cell.borrow_mut()(value, cond_arg),
2623 );
2624 }
2625 return rec_apply(recv, |value| eval_item(&value, arg));
2626 }
2627 BuiltinMethod::TracePath => {
2628 let arg = args
2629 .first()
2630 .ok_or_else(|| EvalError("trace_path: requires predicate".into()))?;
2631 return trace_path_apply(recv, |value| eval_item(value, arg));
2632 }
2633 BuiltinMethod::Fanout => {
2634 return fanout_apply(&recv, args.len(), |value, idx| eval_item(value, &args[idx]));
2635 }
2636 BuiltinMethod::ZipShape => {
2637 if args.is_empty() {
2642 return zip_shape_obj_apply(&recv)
2643 .ok_or_else(|| EvalError("zip_shape: expected object receiver".into()));
2644 }
2645 if args.len() == 1 {
2650 if let Arg::Pos(Expr::Object(fields)) = &args[0] {
2651 let all_short = fields.iter().all(|f| {
2652 matches!(f, crate::parse::ast::ObjField::Short(_))
2653 });
2654 if all_short {
2655 let obj = eval_arg(&args[0])?;
2656 return zip_shape_obj_apply(&obj).ok_or_else(|| {
2657 EvalError("zip_shape: expected object receiver".into())
2658 });
2659 }
2660 }
2661 }
2662 let mut names = Vec::with_capacity(args.len());
2663 for arg in args {
2664 let name: Arc<str> = match arg {
2665 Arg::Named(n, _) => Arc::from(n.as_str()),
2666 Arg::Pos(Expr::Ident(n)) => Arc::from(n.as_str()),
2667 _ => {
2668 return Err(EvalError(
2669 "zip_shape: args must be `name: expr` or bare identifier".into(),
2670 ))
2671 }
2672 };
2673 names.push(name);
2674 }
2675 return zip_shape_apply(&recv, &names, |value, idx| {
2676 eval_item(value, &args[idx])
2677 });
2678 }
2679 BuiltinMethod::GroupShape => {
2680 if args.is_empty() {
2685 return group_shape_by_keys_apply(recv)
2686 .ok_or_else(|| EvalError("group_shape: expected array".into()));
2687 }
2688 if args.len() == 1 {
2692 let key_arg = &args[0];
2693 return group_shape_apply(recv, |value, idx| {
2694 if idx == 0 {
2695 eval_item(&value, key_arg)
2696 } else {
2697 Ok(value)
2698 }
2699 });
2700 }
2701 let key_arg = &args[0];
2702 let shape_arg = &args[1];
2703 return group_shape_apply(recv, |value, idx| {
2704 if idx == 0 {
2705 eval_item(&value, key_arg)
2706 } else {
2707 eval_item(&value, shape_arg)
2708 }
2709 });
2710 }
2711 BuiltinMethod::Sort => {
2712 if args.is_empty() {
2713 return sort_apply(recv);
2714 }
2715 let mut key_args = Vec::with_capacity(args.len());
2716 let mut desc = Vec::with_capacity(args.len());
2717 for arg in args {
2718 match arg {
2719 Arg::Pos(Expr::Lambda { params, .. })
2720 | Arg::Named(_, Expr::Lambda { params, .. })
2721 if params.len() == 2 =>
2722 {
2723 return sort_comparator_apply(recv, |left, right| {
2724 eval_pair(left, right, arg)
2725 });
2726 }
2727 Arg::Pos(Expr::UnaryNeg(inner)) => {
2728 desc.push(true);
2729 key_args.push(Arg::Pos((**inner).clone()));
2730 }
2731 Arg::Pos(e) => {
2732 desc.push(false);
2733 key_args.push(Arg::Pos(e.clone()));
2734 }
2735 Arg::Named(name, Expr::UnaryNeg(inner)) => {
2736 desc.push(true);
2737 key_args.push(Arg::Named(name.clone(), (**inner).clone()));
2738 }
2739 Arg::Named(name, e) => {
2740 desc.push(false);
2741 key_args.push(Arg::Named(name.clone(), e.clone()));
2742 }
2743 }
2744 }
2745 return sort_by_apply(recv, &desc, |item, idx| eval_item(item, &key_args[idx]));
2746 }
2747 BuiltinMethod::Flatten => {
2748 let depth = if args.is_empty() {
2749 1
2750 } else {
2751 i64_arg!(0)?.max(0) as usize
2752 };
2753 BuiltinCall::new(method, BuiltinArgs::Usize(depth))
2754 }
2755 BuiltinMethod::First | BuiltinMethod::Last => {
2756 let n = if args.is_empty() { 1 } else { i64_arg!(0)? };
2757 BuiltinCall::new(method, BuiltinArgs::I64(n))
2758 }
2759 BuiltinMethod::Nth => BuiltinCall::new(method, BuiltinArgs::I64(i64_arg!(0)?)),
2760 BuiltinMethod::Take | BuiltinMethod::Skip => {
2761 BuiltinCall::new(method, BuiltinArgs::Usize(i64_arg!(0)?.max(0) as usize))
2762 }
2763 BuiltinMethod::Append | BuiltinMethod::Prepend | BuiltinMethod::Set => {
2764 let item = if args.is_empty() {
2765 Val::Null
2766 } else {
2767 arg_val!(0)?
2768 };
2769 BuiltinCall::new(method, BuiltinArgs::Val(item))
2770 }
2771 BuiltinMethod::Or => {
2772 let default = if args.is_empty() {
2773 Val::Null
2774 } else {
2775 arg_val!(0)?
2776 };
2777 BuiltinCall::new(method, BuiltinArgs::Val(default))
2778 }
2779 BuiltinMethod::Includes | BuiltinMethod::Index | BuiltinMethod::IndicesOf => {
2780 BuiltinCall::new(method, BuiltinArgs::Val(arg_val!(0)?))
2781 }
2782 BuiltinMethod::Diff | BuiltinMethod::Intersect | BuiltinMethod::Union => {
2783 BuiltinCall::new(method, BuiltinArgs::ValVec(vec_arg!(0)?))
2784 }
2785 BuiltinMethod::Window
2786 | BuiltinMethod::Chunk
2787 | BuiltinMethod::RollingSum
2788 | BuiltinMethod::RollingAvg
2789 | BuiltinMethod::RollingMin
2790 | BuiltinMethod::RollingMax => {
2791 BuiltinCall::new(method, BuiltinArgs::Usize(i64_arg!(0)?.max(0) as usize))
2792 }
2793 BuiltinMethod::Lag | BuiltinMethod::Lead => {
2794 let n = if args.is_empty() {
2795 1
2796 } else {
2797 i64_arg!(0)?.max(0) as usize
2798 };
2799 BuiltinCall::new(method, BuiltinArgs::Usize(n))
2800 }
2801 BuiltinMethod::Merge
2802 | BuiltinMethod::DeepMerge
2803 | BuiltinMethod::Defaults
2804 | BuiltinMethod::Rename => BuiltinCall::new(method, BuiltinArgs::Val(arg_val!(0)?)),
2805 BuiltinMethod::ParseInt if !args.is_empty() => {
2806 let radix = i64_arg!(0)?;
2811 BuiltinCall::new(method, BuiltinArgs::Usize(radix.max(0) as usize))
2812 }
2813 BuiltinMethod::ToCsv | BuiltinMethod::ToTsv if !args.is_empty() => {
2814 let headers = str_vec_arg!(0)?;
2817 BuiltinCall::new(method, BuiltinArgs::StrVec(headers))
2818 }
2819 BuiltinMethod::Remove => match args.first() {
2820 Some(Arg::Pos(Expr::Lambda { .. })) | Some(Arg::Named(_, Expr::Lambda { .. })) => {
2827 return remove_predicate_apply(recv, |item| eval_item(item, &args[0]));
2828 }
2829 Some(arg) if arg_uses_current(arg) => {
2830 return remove_predicate_apply(recv, |item| eval_item(item, &args[0]));
2831 }
2832 Some(_) => BuiltinCall::new(method, BuiltinArgs::Val(arg_val!(0)?)),
2833 None => return Err(EvalError("remove: requires arg".into())),
2834 },
2835 BuiltinMethod::Zip => {
2836 let other = args
2837 .first()
2838 .map(|arg| eval_arg(arg))
2839 .transpose()?
2840 .unwrap_or_else(|| Val::arr(Vec::new()));
2841 return zip_apply(recv, other);
2842 }
2843 BuiltinMethod::ZipLongest => {
2844 let mut other = Val::arr(Vec::new());
2845 let mut fill = Val::Null;
2846 for arg in args {
2847 match arg {
2848 Arg::Pos(_) => other = eval_arg(arg)?,
2849 Arg::Named(n, _) if n == "fill" => fill = eval_arg(arg)?,
2850 Arg::Named(_, _) => {}
2851 }
2852 }
2853 return zip_longest_apply(recv, other, fill);
2854 }
2855 BuiltinMethod::EquiJoin => {
2856 let other = arg_val!(0)?;
2857 let lhs_key = str_arg!(1)?;
2858 let rhs_key = str_arg!(2)?;
2859 return equi_join_apply(recv, other, &lhs_key, &rhs_key);
2860 }
2861 BuiltinMethod::Pivot => {
2862 return pivot_apply(recv, args.len(), |item, idx| match &args[idx] {
2863 Arg::Pos(Expr::Str(s)) | Arg::Named(_, Expr::Str(s)) => {
2864 Ok(item.get_field(s.as_str()))
2865 }
2866 arg => eval_item(item, arg),
2867 });
2868 }
2869 BuiltinMethod::Slice => {
2870 let start = i64_arg!(0)?;
2871 let end = if args.len() > 1 {
2872 Some(i64_arg!(1)?)
2873 } else {
2874 None
2875 };
2876 BuiltinCall::new(
2877 method,
2878 BuiltinArgs::I64Opt {
2879 first: start,
2880 second: end,
2881 },
2882 )
2883 }
2884 BuiltinMethod::Join => {
2885 let sep = if args.is_empty() {
2886 Arc::from("")
2887 } else {
2888 str_arg!(0)?
2889 };
2890 BuiltinCall::new(method, BuiltinArgs::Str(sep))
2891 }
2892 BuiltinMethod::FlattenKeys | BuiltinMethod::UnflattenKeys if args.is_empty() => {
2893 BuiltinCall::new(method, BuiltinArgs::Str(Arc::from(".")))
2894 }
2895 BuiltinMethod::Missing if args.len() >= 2 => {
2899 let keys = (0..args.len())
2900 .map(|i| str_arg!(i))
2901 .collect::<Result<Vec<_>, _>>()?;
2902 BuiltinCall::new(method, BuiltinArgs::StrVec(keys))
2903 }
2904 BuiltinMethod::GetPath | BuiltinMethod::HasPath => {
2905 BuiltinCall::new(
2906 method,
2907 BuiltinArgs::Path(parse_path_segs(str_arg!(0)?.as_ref()).into()),
2908 )
2909 }
2910 BuiltinMethod::HasAll => BuiltinCall::new(method, BuiltinArgs::Val(arg_val!(0)?)),
2911 BuiltinMethod::Has
2912 | BuiltinMethod::HasKey
2913 | BuiltinMethod::Missing
2914 | BuiltinMethod::Explode
2915 | BuiltinMethod::Implode
2916 | BuiltinMethod::DelPath
2917 | BuiltinMethod::FlattenKeys
2918 | BuiltinMethod::UnflattenKeys
2919 | BuiltinMethod::StartsWith
2920 | BuiltinMethod::EndsWith
2921 | BuiltinMethod::IndexOf
2922 | BuiltinMethod::LastIndexOf
2923 | BuiltinMethod::StripPrefix
2924 | BuiltinMethod::StripSuffix
2925 | BuiltinMethod::Matches
2926 | BuiltinMethod::Scan
2927 | BuiltinMethod::Split
2928 | BuiltinMethod::ReMatch
2929 | BuiltinMethod::ReMatchFirst
2930 | BuiltinMethod::ReMatchAll
2931 | BuiltinMethod::ReCaptures
2932 | BuiltinMethod::ReCapturesAll
2933 | BuiltinMethod::ReSplit => BuiltinCall::new(method, BuiltinArgs::Str(str_arg!(0)?)),
2934 BuiltinMethod::Replace
2935 | BuiltinMethod::ReplaceAll
2936 | BuiltinMethod::ReReplace
2937 | BuiltinMethod::ReReplaceAll => BuiltinCall::new(
2938 method,
2939 BuiltinArgs::StrPair {
2940 first: str_arg!(0)?,
2941 second: str_arg!(1)?,
2942 },
2943 ),
2944 BuiltinMethod::ContainsAny | BuiltinMethod::ContainsAll => {
2945 BuiltinCall::new(method, BuiltinArgs::StrVec(str_vec_arg!(0)?))
2946 }
2947 BuiltinMethod::Pick => {
2948 let mut specs = Vec::with_capacity(args.len());
2949 for arg in args {
2950 let resolved: Option<(Arc<str>, Arc<str>)> = match arg {
2951 Arg::Pos(Expr::Ident(s)) => {
2952 let key: Arc<str> = Arc::from(s.as_str());
2953 Some((key.clone(), key))
2954 }
2955 Arg::Pos(_) => match eval_arg(arg)? {
2956 Val::Str(s) => {
2957 let out_key: Arc<str> = if s.contains('.') || s.contains('[') {
2958 match parse_path_segs(&s).first() {
2959 Some(PathSeg::Field(f)) => Arc::from(f.as_str()),
2960 Some(PathSeg::Index(i)) => Arc::from(i.to_string().as_str()),
2961 None => s.clone(),
2962 }
2963 } else {
2964 s.clone()
2965 };
2966 Some((out_key, s))
2967 }
2968 _ => None,
2969 },
2970 Arg::Named(alias, Expr::Ident(src)) => {
2971 Some((Arc::from(alias.as_str()), Arc::from(src.as_str())))
2972 }
2973 Arg::Named(alias, _) => match eval_arg(arg)? {
2974 Val::Str(s) => Some((Arc::from(alias.as_str()), s)),
2975 _ => None,
2976 },
2977 };
2978 let Some((out_key, src)) = resolved else {
2979 continue;
2980 };
2981 let source = if src.contains('.') || src.contains('[') {
2982 PickSource::Path(parse_path_segs(&src))
2983 } else {
2984 PickSource::Field(src)
2985 };
2986 specs.push(PickSpec { out_key, source });
2987 }
2988 return pick_specs_apply(&recv, &specs)
2989 .ok_or_else(|| EvalError("pick: expected object or array of objects".into()));
2990 }
2991 BuiltinMethod::Omit => {
2992 let mut keys = Vec::with_capacity(args.len());
2993 for idx in 0..args.len() {
2994 keys.push(str_arg!(idx)?);
2995 }
2996 BuiltinCall::new(method, BuiltinArgs::StrVec(keys))
2997 }
2998 BuiltinMethod::Repeat | BuiltinMethod::Indent => {
2999 let prefix_arg = if matches!(method, BuiltinMethod::Indent) && !args.is_empty() {
3004 match &args[0] {
3005 Arg::Pos(Expr::Str(s)) => Some(Arc::<str>::from(s.as_str())),
3006 Arg::Named(_, Expr::Str(s)) => Some(Arc::<str>::from(s.as_str())),
3007 _ => None,
3008 }
3009 } else {
3010 None
3011 };
3012 if let Some(prefix) = prefix_arg {
3013 BuiltinCall::new(method, BuiltinArgs::Str(prefix))
3014 } else {
3015 let n = if args.is_empty() {
3016 if matches!(method, BuiltinMethod::Indent) {
3017 2
3018 } else {
3019 1
3020 }
3021 } else {
3022 i64_arg!(0)?.max(0) as usize
3023 };
3024 BuiltinCall::new(method, BuiltinArgs::Usize(n))
3025 }
3026 }
3027 BuiltinMethod::PadLeft | BuiltinMethod::PadRight | BuiltinMethod::Center => {
3028 BuiltinCall::new(
3029 method,
3030 BuiltinArgs::Pad {
3031 width: i64_arg!(0)?.max(0) as usize,
3032 fill: fill_arg!(1)?,
3033 },
3034 )
3035 }
3036 BuiltinMethod::SetPath => {
3037 return set_path_apply(&recv, &str_arg!(0)?, &arg_val!(1)?)
3038 .ok_or_else(|| EvalError("set_path: builtin unsupported".into()));
3039 }
3040 BuiltinMethod::DelPaths => {
3041 let mut paths = Vec::with_capacity(args.len());
3042 for idx in 0..args.len() {
3043 paths.push(str_arg!(idx)?);
3044 }
3045 return del_paths_apply(&recv, &paths)
3046 .ok_or_else(|| EvalError("del_paths: builtin unsupported".into()));
3047 }
3048 _ => {
3049 return Err(EvalError(format!(
3050 "{}: builtin not migrated to builtins.rs AST adapter",
3051 name
3052 )));
3053 }
3054 };
3055
3056 call.try_apply(&recv)?
3057 .ok_or_else(|| EvalError(format!("{}: builtin unsupported", name)))
3058}
3059
3060fn arg_uses_current(arg: &crate::parse::ast::Arg) -> bool {
3066 use crate::parse::ast::{Arg, Expr};
3067 fn walk(e: &Expr) -> bool {
3068 match e {
3069 Expr::Current => true,
3070 Expr::Lambda { .. } => false, Expr::Chain(base, _) => walk(base),
3072 Expr::UnaryNeg(x) | Expr::Not(x) => walk(x),
3073 Expr::BinOp(l, _, r) => walk(l) || walk(r),
3074 Expr::Coalesce(l, r) => walk(l) || walk(r),
3075 Expr::IfElse { cond, then_, else_ } => walk(cond) || walk(then_) || walk(else_),
3076 Expr::Try { body, default } => walk(body) || walk(default),
3077 Expr::Cast { expr, .. } => walk(expr),
3078 Expr::FString(parts) => parts.iter().any(|p| match p {
3079 crate::parse::ast::FStringPart::Interp { expr, .. } => walk(expr),
3080 _ => false,
3081 }),
3082 Expr::Let { init, body, .. } => walk(init) || walk(body),
3083 _ => false,
3087 }
3088 }
3089 match arg {
3090 Arg::Pos(e) | Arg::Named(_, e) => walk(e),
3091 }
3092}
3093
3094pub(crate) fn eval_builtin_no_args(recv: Val, name: &str) -> Result<Val, EvalError> {
3097 eval_builtin_method(
3098 recv,
3099 name,
3100 &[],
3101 |_| {
3102 Err(EvalError(format!(
3103 "{}: unexpected argument evaluation",
3104 name
3105 )))
3106 },
3107 |_, _| Err(EvalError(format!("{}: unexpected item evaluation", name))),
3108 |_, _, _| Err(EvalError(format!("{}: unexpected pair evaluation", name))),
3109 )
3110}
3111
3112impl BuiltinMethod {
3113 #[inline]
3116 pub fn is_pipeline_element_method(self) -> bool {
3117 crate::builtins::registry::pipeline_element(crate::builtins::registry::BuiltinId::from_method(
3118 self,
3119 ))
3120 }
3121}
3122
3123
3124pub mod ops;
3125
3126pub(crate) mod builtin;
3127pub(crate) mod defs;
3128#[cfg_attr(not(test), allow(dead_code))]
3129pub(crate) mod helpers;
3130pub(crate) mod registry;
3131
3132pub use ops::array::*;
3133pub use ops::collection::*;
3134pub use ops::misc::*;
3135pub use ops::path::*;
3136pub use ops::regex::*;
3137pub use ops::schema::*;
3138pub use ops::string::*;