1#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
6#[non_exhaustive]
7pub enum Category {
8 Logical,
10 String,
12 Math,
14 DateTime,
16 Reporting,
18 Aggregate,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub struct FunctionInfo {
25 pub name: &'static str,
27 pub category: Category,
29 pub signature: &'static str,
31 pub description: &'static str,
33}
34
35macro_rules! functions {
36 ($($variant:ident $name:literal $min:literal $max:literal $cat:ident $sig:literal $doc:literal;)*) => {
37 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
39 #[non_exhaustive]
40 #[allow(missing_docs, reason = "documented by the catalog, see Function::info")]
41 pub enum Function { $($variant,)* }
42
43 const FUNCTIONS: &[(Function, usize, usize, FunctionInfo)] = &[
44 $((Function::$variant, $min, $max, FunctionInfo {
45 name: $name, category: Category::$cat, signature: $sig, description: $doc,
46 }),)*
47 ];
48 };
49}
50
51functions! {
52 Iif "Iif" 3 255 Logical "Iif(cond, value, ..., else)" "The value of the first condition that holds, else the last argument.";
54 IsNull "IsNull" 1 2 Logical "IsNull(value[, fallback])" "With one argument, whether it is null; with two, the first that is not null.";
55 IsNullOrEmpty "IsNullOrEmpty" 1 1 Logical "IsNullOrEmpty(text)" "Whether the text is null or empty.";
56 Ascii "Ascii" 1 1 String "Ascii(text)" "The code of the first character.";
58 Char "Char" 1 1 String "Char(code)" "The character with this code.";
59 CharIndex "CharIndex" 2 3 String "CharIndex(needle, text[, start])" "The position of needle in text, or -1.";
60 Concat "Concat" 1 255 String "Concat(a, b, ...)" "Joins values as text.";
61 Contains "Contains" 2 2 String "Contains(text, part)" "Whether text contains part.";
62 EndsWith "EndsWith" 2 2 String "EndsWith(text, part)" "Whether text ends with part.";
63 Insert "Insert" 3 3 String "Insert(text, position, part)" "Inserts part at a position.";
64 Len "Len" 1 1 String "Len(text)" "The number of characters.";
65 Lower "Lower" 1 1 String "Lower(text)" "Lowercase.";
66 NewLine "NewLine" 0 0 String "NewLine()" "A line break.";
67 PadLeft "PadLeft" 2 3 String "PadLeft(text, width[, char])" "Pads on the left up to a width.";
68 PadRight "PadRight" 2 3 String "PadRight(text, width[, char])" "Pads on the right up to a width.";
69 Remove "Remove" 2 3 String "Remove(text, start[, count])" "Removes characters.";
70 Replace "Replace" 3 3 String "Replace(text, old, new)" "Replaces every occurrence.";
71 Reverse "Reverse" 1 1 String "Reverse(text)" "The characters in reverse order.";
72 StartsWith "StartsWith" 2 2 String "StartsWith(text, part)" "Whether text starts with part.";
73 Substring "Substring" 2 3 String "Substring(text, start[, count])" "Part of a text, from position 0.";
74 ToStr "ToStr" 1 1 String "ToStr(value)" "The value as text.";
75 Trim "Trim" 1 1 String "Trim(text)" "Removes spaces around the text.";
76 Upper "Upper" 1 1 String "Upper(text)" "Uppercase.";
77 Abs "Abs" 1 1 Math "Abs(x)" "Absolute value.";
79 Acos "Acos" 1 1 Math "Acos(x)" "Arc cosine.";
80 Asin "Asin" 1 1 Math "Asin(x)" "Arc sine.";
81 Atn "Atn" 1 1 Math "Atn(x)" "Arc tangent.";
82 Atn2 "Atn2" 2 2 Math "Atn2(y, x)" "Arc tangent of y/x.";
83 BigMul "BigMul" 2 2 Math "BigMul(a, b)" "The product of two integers.";
84 Ceiling "Ceiling" 1 1 Math "Ceiling(x)" "The next integer up.";
85 Cos "Cos" 1 1 Math "Cos(x)" "Cosine.";
86 Cosh "Cosh" 1 1 Math "Cosh(x)" "Hyperbolic cosine.";
87 Exp "Exp" 1 1 Math "Exp(x)" "e to the power x.";
88 Floor "Floor" 1 1 Math "Floor(x)" "The next integer down.";
89 Log "Log" 1 2 Math "Log(x[, base])" "Logarithm, natural by default.";
90 Log10 "Log10" 1 1 Math "Log10(x)" "Base 10 logarithm.";
91 Max2 "Max" 2 2 Math "Max(a, b)" "The larger of two values.";
92 Min2 "Min" 2 2 Math "Min(a, b)" "The smaller of two values.";
93 Power "Power" 2 2 Math "Power(x, y)" "x to the power y.";
94 Rnd "Rnd" 0 0 Math "Rnd()" "A random number from 0 up to 1.";
95 Round "Round" 1 2 Math "Round(x[, digits])" "Rounds halves away from zero.";
96 Sign "Sign" 1 1 Math "Sign(x)" "-1, 0 or 1.";
97 Sin "Sin" 1 1 Math "Sin(x)" "Sine.";
98 Sinh "Sinh" 1 1 Math "Sinh(x)" "Hyperbolic sine.";
99 Sqr "Sqr" 1 1 Math "Sqr(x)" "Square root.";
100 Sqrt "Sqrt" 1 1 Math "Sqrt(x)" "Square root.";
101 Tan "Tan" 1 1 Math "Tan(x)" "Tangent.";
102 Tanh "Tanh" 1 1 Math "Tanh(x)" "Hyperbolic tangent.";
103 ToDecimal "ToDecimal" 1 1 Math "ToDecimal(x)" "The value as a number.";
104 ToDouble "ToDouble" 1 1 Math "ToDouble(x)" "The value as a number.";
105 ToFloat "ToFloat" 1 1 Math "ToFloat(x)" "The value as a number.";
106 ToInt "ToInt" 1 1 Math "ToInt(x)" "The value as an integer, truncated.";
107 ToLong "ToLong" 1 1 Math "ToLong(x)" "The value as an integer, truncated.";
108 ToNumber "ToNumber" 1 1 Math "ToNumber(x)" "The value as a number.";
109 AddDays "AddDays" 2 2 DateTime "AddDays(date, n)" "n days later.";
111 AddHours "AddHours" 2 2 DateTime "AddHours(date, n)" "n hours later.";
112 AddMinutes "AddMinutes" 2 2 DateTime "AddMinutes(date, n)" "n minutes later.";
113 AddMonths "AddMonths" 2 2 DateTime "AddMonths(date, n)" "n months later.";
114 AddSeconds "AddSeconds" 2 2 DateTime "AddSeconds(date, n)" "n seconds later.";
115 AddYears "AddYears" 2 2 DateTime "AddYears(date, n)" "n years later.";
116 DateDiffDay "DateDiffDay" 2 2 DateTime "DateDiffDay(from, to)" "Day boundaries between two dates.";
117 DateDiffHour "DateDiffHour" 2 2 DateTime "DateDiffHour(from, to)" "Whole hours between two dates.";
118 DateDiffMinute "DateDiffMinute" 2 2 DateTime "DateDiffMinute(from, to)" "Whole minutes between two dates.";
119 DateDiffMonth "DateDiffMonth" 2 2 DateTime "DateDiffMonth(from, to)" "Month boundaries between two dates.";
120 DateDiffSecond "DateDiffSecond" 2 2 DateTime "DateDiffSecond(from, to)" "Whole seconds between two dates.";
121 DateDiffYear "DateDiffYear" 2 2 DateTime "DateDiffYear(from, to)" "Year boundaries between two dates.";
122 DateTimeFromParts "DateTimeFromParts" 3 6 DateTime "DateTimeFromParts(y, m, d[, h, min, s])" "A date from its parts.";
123 GetDate "GetDate" 1 1 DateTime "GetDate(date)" "The date without the time.";
125 GetDay "GetDay" 1 1 DateTime "GetDay(date)" "The day of the month.";
126 GetDayOfWeek "GetDayOfWeek" 1 1 DateTime "GetDayOfWeek(date)" "The day of the week, 0 is Sunday.";
127 GetDayOfYear "GetDayOfYear" 1 1 DateTime "GetDayOfYear(date)" "The day of the year, from 1.";
128 GetHour "GetHour" 1 1 DateTime "GetHour(date)" "The hour.";
129 GetMinute "GetMinute" 1 1 DateTime "GetMinute(date)" "The minute.";
130 GetMonth "GetMonth" 1 1 DateTime "GetMonth(date)" "The month, 1 to 12.";
131 GetSecond "GetSecond" 1 1 DateTime "GetSecond(date)" "The second.";
132 GetYear "GetYear" 1 1 DateTime "GetYear(date)" "The year.";
133 Now "Now" 0 0 DateTime "Now()" "The current date and time.";
134 Today "Today" 0 0 DateTime "Today()" "The current date.";
135 UtcNow "UtcNow" 0 0 DateTime "UtcNow()" "The current date and time; the environment's clock.";
136 LocalDateTimeToday "LocalDateTimeToday" 0 0 DateTime "LocalDateTimeToday()" "The start of today.";
138 LocalDateTimeNow "LocalDateTimeNow" 0 0 DateTime "LocalDateTimeNow()" "Now.";
139 LocalDateTimeTomorrow "LocalDateTimeTomorrow" 0 0 DateTime "LocalDateTimeTomorrow()" "The start of tomorrow.";
140 LocalDateTimeYesterday "LocalDateTimeYesterday" 0 0 DateTime "LocalDateTimeYesterday()" "The start of yesterday.";
141 LocalDateTimeDayAfterTomorrow "LocalDateTimeDayAfterTomorrow" 0 0 DateTime "LocalDateTimeDayAfterTomorrow()" "The start of the day after tomorrow.";
142 LocalDateTimeThisWeek "LocalDateTimeThisWeek" 0 0 DateTime "LocalDateTimeThisWeek()" "The start of this week (Sunday).";
143 LocalDateTimeLastWeek "LocalDateTimeLastWeek" 0 0 DateTime "LocalDateTimeLastWeek()" "The start of last week.";
144 LocalDateTimeNextWeek "LocalDateTimeNextWeek" 0 0 DateTime "LocalDateTimeNextWeek()" "The start of next week.";
145 LocalDateTimeTwoWeeksAway "LocalDateTimeTwoWeeksAway" 0 0 DateTime "LocalDateTimeTwoWeeksAway()" "The start of the week after next.";
146 LocalDateTimeThisMonth "LocalDateTimeThisMonth" 0 0 DateTime "LocalDateTimeThisMonth()" "The first day of this month.";
147 LocalDateTimeLastMonth "LocalDateTimeLastMonth" 0 0 DateTime "LocalDateTimeLastMonth()" "The first day of last month.";
148 LocalDateTimeNextMonth "LocalDateTimeNextMonth" 0 0 DateTime "LocalDateTimeNextMonth()" "The first day of next month.";
149 LocalDateTimeTwoMonthsAway "LocalDateTimeTwoMonthsAway" 0 0 DateTime "LocalDateTimeTwoMonthsAway()" "The first day of the month after next.";
150 LocalDateTimeThisYear "LocalDateTimeThisYear" 0 0 DateTime "LocalDateTimeThisYear()" "The first day of this year.";
151 LocalDateTimeLastYear "LocalDateTimeLastYear" 0 0 DateTime "LocalDateTimeLastYear()" "The first day of last year.";
152 LocalDateTimeNextYear "LocalDateTimeNextYear" 0 0 DateTime "LocalDateTimeNextYear()" "The first day of next year.";
153 LocalDateTimeTwoYearsAway "LocalDateTimeTwoYearsAway" 0 0 DateTime "LocalDateTimeTwoYearsAway()" "The first day of the year after next.";
154 LocalDateTimeYearBeforeToday "LocalDateTimeYearBeforeToday" 0 0 DateTime "LocalDateTimeYearBeforeToday()" "Today, one year ago.";
155 InDateRange "InDateRange" 3 3 DateTime "InDateRange(date, from, to)" "Whether a date's day is in a range, ends included.";
157 InTimeRange "InTimeRange" 3 3 DateTime "InTimeRange(date, from, to)" "Whether a time of day is in a range, which may span midnight.";
158 OutOfTimeRange "OutOfTimeRange" 3 3 DateTime "OutOfTimeRange(date, from, to)" "Whether a time of day is outside a range.";
159 IsSameDay "IsSameDay" 2 255 DateTime "IsSameDay(date, other, ...)" "Whether a date falls on the same day as any other.";
160 IsThisWeek "IsThisWeek" 1 1 DateTime "IsThisWeek(date)" "Whether the date is in this week.";
161 IsThisMonth "IsThisMonth" 1 1 DateTime "IsThisMonth(date)" "Whether the date is in this month.";
162 IsLastMonth "IsLastMonth" 1 1 DateTime "IsLastMonth(date)" "Whether the date is in last month.";
163 IsNextMonth "IsNextMonth" 1 1 DateTime "IsNextMonth(date)" "Whether the date is in next month.";
164 IsThisYear "IsThisYear" 1 1 DateTime "IsThisYear(date)" "Whether the date is in this year.";
165 IsLastYear "IsLastYear" 1 1 DateTime "IsLastYear(date)" "Whether the date is in last year.";
166 IsNextYear "IsNextYear" 1 1 DateTime "IsNextYear(date)" "Whether the date is in next year.";
167 IsYearToDate "IsYearToDate" 1 1 DateTime "IsYearToDate(date)" "Whether the date is from the start of this year up to today.";
168 IsJanuary "IsJanuary" 1 1 DateTime "IsJanuary(date)" "Whether the date is in January.";
169 IsFebruary "IsFebruary" 1 1 DateTime "IsFebruary(date)" "Whether the date is in February.";
170 IsMarch "IsMarch" 1 1 DateTime "IsMarch(date)" "Whether the date is in March.";
171 IsApril "IsApril" 1 1 DateTime "IsApril(date)" "Whether the date is in April.";
172 IsMay "IsMay" 1 1 DateTime "IsMay(date)" "Whether the date is in May.";
173 IsJune "IsJune" 1 1 DateTime "IsJune(date)" "Whether the date is in June.";
174 IsJuly "IsJuly" 1 1 DateTime "IsJuly(date)" "Whether the date is in July.";
175 IsAugust "IsAugust" 1 1 DateTime "IsAugust(date)" "Whether the date is in August.";
176 IsSeptember "IsSeptember" 1 1 DateTime "IsSeptember(date)" "Whether the date is in September.";
177 IsOctober "IsOctober" 1 1 DateTime "IsOctober(date)" "Whether the date is in October.";
178 IsNovember "IsNovember" 1 1 DateTime "IsNovember(date)" "Whether the date is in November.";
179 IsDecember "IsDecember" 1 1 DateTime "IsDecember(date)" "Whether the date is in December.";
180 FormatString "FormatString" 1 255 Reporting "FormatString('{0:C2}', value, ...)" "Formats values with .NET-style format strings.";
182 Rgb "Rgb" 3 3 Reporting "Rgb(r, g, b)" "A color as #rrggbb text.";
183 Argb "Argb" 4 4 Reporting "Argb(a, r, g, b)" "A color as #rrggbbaa text.";
184 PageNumber "PageNumber" 0 0 Reporting "PageNumber()" "The page being printed.";
185 PageCount "PageCount" 0 0 Reporting "PageCount()" "The number of pages.";
186 CurrentRowIndexInGroup "CurrentRowIndexInGroup" 0 0 Reporting "CurrentRowIndexInGroup()" "The row's position in its group, from 0.";
187 PrevRowColumnValue "PrevRowColumnValue" 1 1 Reporting "PrevRowColumnValue(value)" "The value on the previous row of the scope.";
188 NextRowColumnValue "NextRowColumnValue" 1 1 Reporting "NextRowColumnValue(value)" "The value on the next row of the scope.";
189}
190
191impl Function {
192 pub fn info(self) -> &'static FunctionInfo {
194 &FUNCTIONS[self as usize].3
196 }
197
198 pub fn name(self) -> &'static str {
200 self.info().name
201 }
202
203 pub fn arity(self) -> (usize, usize) {
205 let e = &FUNCTIONS[self as usize];
206 (e.1, e.2)
207 }
208
209 pub fn from_name(name: &str) -> Option<Self> {
212 FUNCTIONS.iter().find(|e| e.3.name.eq_ignore_ascii_case(name)).map(|e| e.0)
213 }
214
215 pub fn all() -> impl Iterator<Item = Function> {
217 FUNCTIONS.iter().map(|e| e.0)
218 }
219}
220
221#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
223#[non_exhaustive]
224#[allow(missing_docs, reason = "documented by Aggregate::info")]
225pub enum Aggregate {
226 Sum,
227 Count,
228 Avg,
229 Min,
230 Max,
231 Exists,
232 Single,
233 Join,
234 First,
235 Last,
236 Median,
237 Var,
238 VarP,
239 StdDev,
240 StdDevP,
241 RunningSum,
242 RecordNumber,
243 Percentage,
244 WeightedAvg,
245 DistinctSum,
246 DistinctCount,
247 DistinctAvg,
248 DistinctVar,
249 DistinctVarP,
250 DistinctStdDev,
251 DistinctStdDevP,
252}
253
254const AGGREGATES: &[(Aggregate, &str, &str, usize, usize, &str, &str)] = &[
256 (Aggregate::Sum, "Sum", "sumSum", 1, 1, "Sum(value)", "The total."),
257 (
258 Aggregate::Count,
259 "Count",
260 "sumCount",
261 0,
262 1,
263 "Count([value])",
264 "The number of rows, or of non-null values.",
265 ),
266 (Aggregate::Avg, "Avg", "sumAvg", 1, 1, "Avg(value)", "The mean."),
267 (Aggregate::Min, "Min", "sumMin", 1, 1, "Min(value)", "The smallest value."),
268 (Aggregate::Max, "Max", "sumMax", 1, 1, "Max(value)", "The largest value."),
269 (
270 Aggregate::Exists,
271 "Exists",
272 "",
273 0,
274 1,
275 "Exists([condition])",
276 "Whether any row exists, or satisfies the condition.",
277 ),
278 (Aggregate::Single, "Single", "", 0, 1, "Single([value])", "The value of the only row; null otherwise."),
279 (Aggregate::Join, "Join", "", 1, 2, "Join(value[, separator])", "The values joined as text."),
280 (Aggregate::First, "First", "", 1, 1, "First(value)", "The value on the first row."),
281 (Aggregate::Last, "Last", "", 1, 1, "Last(value)", "The value on the last row."),
282 (Aggregate::Median, "Median", "sumMedian", 1, 1, "Median(value)", "The middle value."),
283 (Aggregate::Var, "Var", "sumVar", 1, 1, "Var(value)", "Sample variance."),
284 (Aggregate::VarP, "VarP", "sumVarP", 1, 1, "VarP(value)", "Population variance."),
285 (Aggregate::StdDev, "StdDev", "sumStdDev", 1, 1, "StdDev(value)", "Sample standard deviation."),
286 (Aggregate::StdDevP, "StdDevP", "sumStdDevP", 1, 1, "StdDevP(value)", "Population standard deviation."),
287 (
288 Aggregate::RunningSum,
289 "RunningSum",
290 "sumRunningSum",
291 1,
292 1,
293 "RunningSum(value)",
294 "The total up to the current row.",
295 ),
296 (
297 Aggregate::RecordNumber,
298 "RecordNumber",
299 "sumRecordNumber",
300 0,
301 0,
302 "RecordNumber()",
303 "The row's number in the scope, from 1.",
304 ),
305 (
306 Aggregate::Percentage,
307 "Percentage",
308 "sumPercentage",
309 1,
310 1,
311 "Percentage(value)",
312 "The row's value as a fraction of the total.",
313 ),
314 (
315 Aggregate::WeightedAvg,
316 "WeightedAvg",
317 "sumWAvg",
318 2,
319 2,
320 "WeightedAvg(value, weight)",
321 "The mean weighted by a second value.",
322 ),
323 (
324 Aggregate::DistinctSum,
325 "DistinctSum",
326 "sumDSum",
327 1,
328 1,
329 "DistinctSum(value)",
330 "The total of the distinct values.",
331 ),
332 (
333 Aggregate::DistinctCount,
334 "DistinctCount",
335 "sumDCount",
336 1,
337 1,
338 "DistinctCount(value)",
339 "The number of distinct values.",
340 ),
341 (
342 Aggregate::DistinctAvg,
343 "DistinctAvg",
344 "sumDAvg",
345 1,
346 1,
347 "DistinctAvg(value)",
348 "The mean of the distinct values.",
349 ),
350 (
351 Aggregate::DistinctVar,
352 "DistinctVar",
353 "sumDVar",
354 1,
355 1,
356 "DistinctVar(value)",
357 "Sample variance of the distinct values.",
358 ),
359 (
360 Aggregate::DistinctVarP,
361 "DistinctVarP",
362 "sumDVarP",
363 1,
364 1,
365 "DistinctVarP(value)",
366 "Population variance of the distinct values.",
367 ),
368 (
369 Aggregate::DistinctStdDev,
370 "DistinctStdDev",
371 "sumDStdDev",
372 1,
373 1,
374 "DistinctStdDev(value)",
375 "Sample standard deviation of the distinct values.",
376 ),
377 (
378 Aggregate::DistinctStdDevP,
379 "DistinctStdDevP",
380 "sumDStdDevP",
381 1,
382 1,
383 "DistinctStdDevP(value)",
384 "Population standard deviation of the distinct values.",
385 ),
386];
387
388impl Aggregate {
389 fn entry(
390 self,
391 ) -> &'static (Aggregate, &'static str, &'static str, usize, usize, &'static str, &'static str) {
392 &AGGREGATES[self as usize]
393 }
394
395 pub fn name(self) -> &'static str {
397 self.entry().1
398 }
399
400 pub fn arity(self) -> (usize, usize) {
402 (self.entry().3, self.entry().4)
403 }
404
405 pub fn info(self) -> FunctionInfo {
407 let e = self.entry();
408 FunctionInfo { name: e.1, category: Category::Aggregate, signature: e.5, description: e.6 }
409 }
410
411 pub fn from_name(name: &str) -> Option<Self> {
413 AGGREGATES
414 .iter()
415 .find(|e| e.1.eq_ignore_ascii_case(name) || (!e.2.is_empty() && e.2.eq_ignore_ascii_case(name)))
416 .map(|e| e.0)
417 }
418
419 pub fn all() -> impl Iterator<Item = Aggregate> {
421 AGGREGATES.iter().map(|e| e.0)
422 }
423}
424
425pub fn catalog() -> Vec<FunctionInfo> {
427 Function::all().map(|f| *f.info()).chain(Aggregate::all().map(Aggregate::info)).collect()
428}
429
430#[cfg(test)]
431mod tests {
432 use super::*;
433
434 #[test]
435 fn tables_match_their_enums() {
436 for (i, f) in Function::all().enumerate() {
437 assert_eq!(f as usize, i, "{f:?}");
438 assert_eq!(Function::from_name(f.name()).map(Function::name), Some(f.name()));
439 assert!(f.info().signature.starts_with(f.name()), "{f:?}");
440 }
441 for (i, a) in Aggregate::all().enumerate() {
442 assert_eq!(a as usize, i, "{a:?}");
443 assert_eq!(Aggregate::from_name(a.name()), Some(a));
444 }
445 assert_eq!(Aggregate::from_name("SUMSUM"), Some(Aggregate::Sum));
446 assert_eq!(Aggregate::from_name("sumWAvg"), Some(Aggregate::WeightedAvg));
447 assert!(catalog().len() > 130);
448 }
449}