1use std::{
3 collections::{HashMap, HashSet},
4 num::TryFromIntError,
5};
6
7#[cfg(feature = "clock")]
8use chrono::Utc;
9use chrono::{DateTime, Duration, FixedOffset};
10use miette::SourceSpan;
11use pest::Parser as _;
12use strumbra::SharedString;
13
14use crate::{
15 ParseError,
16 enc_regex::EncodableRegex,
17 linker::{AlignFunction, ComputeFunction, GroupFunction, MapFunction},
18 parser::{self, MPLParser, ParseParamError, Rule},
19 tags::TagValue,
20 time::{Resolution, ResolutionError},
21 types::{BucketSpec, BucketType, Dataset, Metric, Parameterized},
22};
23
24mod fmt;
25#[cfg(test)]
26mod tests;
27
28#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
30#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
31#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
32pub struct MetricId {
33 pub dataset: Parameterized<Dataset>,
35 pub metric: Metric,
37}
38
39#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
41#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
42#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
43pub enum TimeUnit {
44 Millisecond,
46 Second,
48 Minute,
50 Hour,
52 Day,
54 Week,
56 Month,
58 Year,
60}
61
62#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
63#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
65#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
66pub struct RelativeTime {
67 pub value: u64,
69 pub unit: TimeUnit,
71}
72
73#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
75#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
76#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
77pub enum Time {
78 Relative(RelativeTime),
80 Timestamp(i64),
82 RFC3339(#[cfg_attr(feature = "wasm", tsify(type = "string"))] DateTime<FixedOffset>),
84 Modifier(String),
86}
87
88#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
90#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
91#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
92pub struct TimeRange {
93 pub start: Time,
95 pub end: Option<Time>,
97}
98
99#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
101#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
102#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
103pub struct Source {
104 pub metric_id: MetricId,
106 pub time: Option<TimeRange>,
108}
109
110#[derive(Debug, thiserror::Error)]
112pub enum ValueError {
113 #[error("Invalid Float")]
115 BadFloat,
116}
117
118#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
120#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
121#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
122pub enum Cmp {
123 Eq(Parameterized<TagValue>),
125 Ne(Parameterized<TagValue>),
127 Gt(Parameterized<TagValue>),
129 Ge(Parameterized<TagValue>),
131 Lt(Parameterized<TagValue>),
133 Le(Parameterized<TagValue>),
135 RegEx(Parameterized<EncodableRegex>),
137 RegExNot(Parameterized<EncodableRegex>),
139 Is(TagType),
141}
142
143#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
145#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
146#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
147pub struct As {
148 pub name: Metric,
150}
151
152#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
154#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
155#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
156pub enum Filter {
157 And(Vec<Filter>),
159 Or(Vec<Filter>),
161 Not(Box<Filter>),
163 Cmp {
165 field: String,
167 rhs: Cmp,
169 },
170}
171
172#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
174#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
175#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
176pub enum FilterOrIfDef {
177 Filter(Filter),
179 Ifdef {
181 param: ParamDeclaration,
183 filter: Filter,
185 },
186}
187
188impl FilterOrIfDef {
189 #[cfg(test)]
190 pub(crate) fn filter(&self) -> &Filter {
191 match self {
192 FilterOrIfDef::Filter(filter) | FilterOrIfDef::Ifdef { filter, .. } => filter,
193 }
194 }
195}
196
197#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
199#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
200#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
201pub struct Mapping {
202 pub function: MapFunction,
204 pub arg: Option<f64>,
206}
207
208#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
210#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
211#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
212pub struct Align {
213 pub function: AlignFunction,
215 pub time: Parameterized<RelativeTime>,
217}
218
219#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
221#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
222#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
223pub struct GroupBy {
224 #[cfg_attr(feature = "wasm", tsify(type = "{ offset: number, length: number }"))]
226 pub span: SourceSpan,
227 pub function: GroupFunction,
229 pub tags: Vec<String>,
231}
232
233#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
235#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
236#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
237pub struct BucketBy {
238 #[cfg_attr(feature = "wasm", tsify(type = "{ offset: number, length: number }"))]
240 pub span: SourceSpan,
241 pub function: BucketType,
243 pub time: Parameterized<RelativeTime>,
245 pub tags: Vec<String>,
247 pub spec: Vec<BucketSpec>,
249}
250
251#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
253#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
254#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
255pub enum Aggregate {
256 Map(Mapping),
258 Align(Align),
260 GroupBy(GroupBy),
262 Bucket(BucketBy),
264 As(As),
266}
267
268#[cfg_attr(feature = "wasm", tsify::declare)]
270#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
271#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
272pub enum DirectiveValue {
273 Ident(String),
275 Int(i64),
277 Float(f64),
279 String(String),
281 Bool(bool),
283 None,
285}
286
287impl DirectiveValue {
288 #[must_use]
290 pub fn as_ident(&self) -> Option<&str> {
291 match self {
292 DirectiveValue::Ident(ident) => Some(ident),
293 _ => None,
294 }
295 }
296 #[must_use]
298 pub fn as_int(&self) -> Option<i64> {
299 match self {
300 DirectiveValue::Int(int) => Some(*int),
301 _ => None,
302 }
303 }
304 #[must_use]
306 pub fn as_float(&self) -> Option<f64> {
307 match self {
308 DirectiveValue::Float(float) => Some(*float),
309 _ => None,
310 }
311 }
312 #[must_use]
314 pub fn as_string(&self) -> Option<&str> {
315 match self {
316 DirectiveValue::String(string) => Some(string),
317 _ => None,
318 }
319 }
320 #[must_use]
322 pub fn as_bool(&self) -> Option<bool> {
323 match self {
324 DirectiveValue::Bool(bool) => Some(*bool),
325 _ => None,
326 }
327 }
328 #[must_use]
330 pub fn is_none(&self) -> bool {
331 matches!(self, DirectiveValue::None)
332 }
333 #[must_use]
335 pub fn is_some(&self) -> bool {
336 !self.is_none()
337 }
338}
339
340#[cfg_attr(feature = "wasm", tsify::declare)]
342#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
343pub enum ParamType {
344 Terminal(TerminalParamType),
346 Optional(TerminalParamType),
348}
349
350impl ParamType {
351 fn is_optional(self) -> bool {
352 matches!(self, ParamType::Optional(_))
353 }
354}
355
356impl std::fmt::Display for ParamType {
357 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358 match self {
359 ParamType::Terminal(t) => t.fmt(f),
360 ParamType::Optional(t) => write!(f, "Option<{t}>"),
361 }
362 }
363}
364
365#[cfg_attr(feature = "wasm", tsify::declare)]
367#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
368pub enum TerminalParamType {
369 Duration,
371 Dataset,
373 Regex,
375 Tag(TagType),
377}
378impl std::fmt::Display for TerminalParamType {
379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
380 match self {
381 TerminalParamType::Dataset => write!(f, "Dataset"),
382 TerminalParamType::Duration => write!(f, "Duration"),
383 TerminalParamType::Regex => write!(f, "Regex"),
384 TerminalParamType::Tag(t) => t.fmt(f),
385 }
386 }
387}
388
389#[cfg_attr(feature = "wasm", tsify::declare)]
391#[cfg_attr(feature = "bincode", derive(bincode::Encode, bincode::Decode))]
392#[derive(Clone, Copy, Debug, Hash, serde::Deserialize, serde::Serialize, PartialEq, Eq)]
393pub enum TagType {
394 String,
396 Int,
398 Float,
400 Bool,
402 Null,
404}
405
406#[cfg(feature = "bincode")]
407#[test]
408fn test_renaming_none_to_null_has_no_bincode_side_effects() {
409 let enc = [4];
410 assert_eq!(
411 (TagType::Null, 1),
412 bincode::decode_from_slice(&enc, bincode::config::standard()).expect("it does ...")
413 );
414}
415
416impl std::fmt::Display for TagType {
417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418 write!(
419 f,
420 "{}",
421 match self {
422 TagType::String => "string",
423 TagType::Int => "int",
424 TagType::Float => "float",
425 TagType::Bool => "bool",
426 TagType::Null => "null",
427 }
428 )
429 }
430}
431
432#[cfg_attr(feature = "wasm", tsify::declare)]
434pub type Directives = HashMap<String, DirectiveValue>;
435
436#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
438#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
439pub struct ParamDeclaration {
440 #[cfg_attr(feature = "wasm", tsify(type = "{ offset: number, length: number }"))]
442 pub span: SourceSpan,
443 pub name: String,
445 pub typ: ParamType,
447}
448
449impl ParamDeclaration {
450 pub(crate) fn typ(&self) -> TerminalParamType {
451 match self.typ {
452 ParamType::Terminal(terminal_param_type) | ParamType::Optional(terminal_param_type) => {
453 terminal_param_type
454 }
455 }
456 }
457
458 pub(crate) fn is_optional(&self) -> bool {
459 self.typ.is_optional()
460 }
461}
462
463#[derive(Debug, Clone, PartialEq)]
465pub enum ParamValue {
466 Dataset(Dataset),
468 Duration(RelativeTime),
470 String(String),
472 Int(i64),
474 Float(f64),
476 Bool(bool),
478 Regex(EncodableRegex),
480}
481
482impl ParamValue {
483 #[must_use]
485 pub fn typ(&self) -> TerminalParamType {
486 match self {
487 ParamValue::Dataset(_) => TerminalParamType::Dataset,
488 ParamValue::Duration(_) => TerminalParamType::Duration,
489 ParamValue::Regex(_) => TerminalParamType::Regex,
490 ParamValue::String(_) => TerminalParamType::Tag(TagType::String),
491 ParamValue::Int(_) => TerminalParamType::Tag(TagType::Int),
492 ParamValue::Float(_) => TerminalParamType::Tag(TagType::Float),
493 ParamValue::Bool(_) => TerminalParamType::Tag(TagType::Bool),
494 }
495 }
496}
497
498#[derive(Debug, Clone, PartialEq)]
500pub struct ProvidedParam {
501 pub name: String,
503 pub value: ParamValue,
505}
506
507impl ProvidedParam {
508 pub fn new(name: impl Into<String>, value: ParamValue) -> Self {
510 Self {
511 name: name.into(),
512 value,
513 }
514 }
515}
516
517#[derive(Debug, Clone, Default)]
519pub struct ProvidedParams {
520 inner: Vec<ProvidedParam>,
521}
522
523#[derive(Debug, thiserror::Error)]
525pub enum ResolveError {
526 #[error("Param ${0} was not provided to the query")]
528 ParamNotProvided(String),
529 #[error(
531 "Param ${name} is defined as `{defined}`, but was used in a context that expected one of: {}",
532 expected.iter().map(ToString::to_string).collect::<Vec<_>>().join(", ")
533 )]
534 InvalidType {
535 name: String,
537 defined: TerminalParamType,
539 expected: Vec<TerminalParamType>,
541 },
542 #[error("Shared string error: {0}")]
544 SharedString(#[from] strumbra::Error),
545}
546
547#[derive(Debug, thiserror::Error)]
549pub enum ParseProvidedParamsError {
550 #[error("Failed to parse the value for ${param_name} as {expected_type}: {err}")]
552 ParseParam {
553 param_name: String,
555 expected_type: ParamType,
557 err: ParseParamError,
559 },
560 #[error("These params were provided more than once: {}", .0.join(", "))]
562 ParamsProvidedMoreThanOnce(Vec<String>),
563 #[error("The following params were declared but not provided: {}", .0.join(", "))]
565 ParamsDeclaredButNotProvided(Vec<String>),
566 #[error("The number of params provided exceeds the upper limit of {0}")]
568 TooManyParamsProvided(usize),
569}
570
571#[derive(Debug, Default)]
573pub struct Warnings {
574 inner: Vec<String>,
575}
576
577impl Warnings {
578 #[must_use]
580 pub fn new() -> Self {
581 Self::default()
582 }
583
584 pub fn push(&mut self, warning: impl Into<String>) {
586 self.inner.push(warning.into());
587 }
588
589 #[must_use]
591 pub fn is_empty(&self) -> bool {
592 self.inner.is_empty()
593 }
594
595 #[must_use]
597 pub fn as_slice(&self) -> &[String] {
598 &self.inner
599 }
600
601 #[must_use]
603 pub fn into_vec(self) -> Vec<String> {
604 self.inner
605 }
606}
607
608impl ProvidedParams {
609 #[must_use]
611 pub fn new(inner: Vec<ProvidedParam>) -> Self {
612 Self { inner }
613 }
614
615 pub fn parse_and_validate(
619 mpl_params: &Params,
620 query_params: &[(String, String)],
621 ) -> Result<(Self, Warnings), ParseProvidedParamsError> {
622 const PREFIX: &str = "param__";
623 const PARAM_COUNT_LIMIT: usize = 128;
624
625 let mut warnings = Warnings::new();
626 let mut defined_more_than_once = HashSet::new();
627 let mut provided_but_not_declared = HashSet::new();
628 let mut seen = HashSet::new();
629
630 let params = query_params
631 .iter()
632 .filter_map(|(name, value)| {
633 if !name.starts_with(PREFIX) {
634 return None;
635 }
636 let name = name.trim_start_matches(PREFIX);
637 if name.is_empty() {
638 return None;
639 }
640
641 Some((name, value))
642 })
643 .take(PARAM_COUNT_LIMIT + 1)
644 .collect::<Vec<(&str, &String)>>();
645
646 if params.len() > PARAM_COUNT_LIMIT {
648 return Err(ParseProvidedParamsError::TooManyParamsProvided(
649 PARAM_COUNT_LIMIT,
650 ));
651 }
652
653 let mut provided_params = Vec::new();
654 for (name, value) in params {
655 if seen.contains(name) {
656 defined_more_than_once.insert(name);
658 continue;
659 }
660 seen.insert(name);
661
662 let Some(mpl_param) = mpl_params.iter().find(|p| p.name == name) else {
664 provided_but_not_declared.insert(name);
665 continue;
666 };
667
668 let parsed = MPLParser::parse(Rule::param_value, value).map_err(|err| {
670 ParseProvidedParamsError::ParseParam {
671 param_name: name.to_string(),
672 expected_type: mpl_param.typ,
673 err: ParseParamError::Parse(ParseError::from(err)),
674 }
675 })?;
676
677 let value = parser::parse_param_value(mpl_param, parsed).map_err(|err| {
679 ParseProvidedParamsError::ParseParam {
680 param_name: name.to_string(),
681 expected_type: mpl_param.typ,
682 err,
683 }
684 })?;
685
686 provided_params.push(ProvidedParam {
687 name: name.to_string(),
688 value,
689 });
690 }
691
692 if !provided_but_not_declared.is_empty() {
693 let mut items = provided_but_not_declared
695 .into_iter()
696 .map(|p| format!("${p}"))
697 .collect::<Vec<String>>();
698 items.sort();
699
700 warnings.push(format!(
702 "These params were provided but not declared: {}",
703 items.join(", ")
704 ));
705 }
706
707 if !defined_more_than_once.is_empty() {
708 let mut items = defined_more_than_once
710 .into_iter()
711 .map(String::from)
712 .collect::<Vec<String>>();
713 items.sort();
714
715 return Err(ParseProvidedParamsError::ParamsProvidedMoreThanOnce(items));
716 }
717
718 let declared_param_names = mpl_params
719 .iter()
720 .filter_map(|p| {
721 if p.typ.is_optional() {
723 None
724 } else {
725 Some(p.name.as_str())
726 }
727 })
728 .collect::<HashSet<&str>>();
729 let declared_but_not_provided = declared_param_names
730 .difference(&seen)
731 .collect::<Vec<&&str>>();
732 if !declared_but_not_provided.is_empty() {
733 let mut items = declared_but_not_provided
735 .into_iter()
736 .map(|s| String::from(*s))
737 .collect::<Vec<String>>();
738 items.sort();
739
740 return Err(ParseProvidedParamsError::ParamsDeclaredButNotProvided(
741 items,
742 ));
743 }
744
745 Ok((ProvidedParams::new(provided_params), warnings))
746 }
747
748 #[must_use]
750 pub fn as_slice(&self) -> &[ProvidedParam] {
751 self.inner.as_slice()
752 }
753
754 fn get_param(&self, name: &str) -> Result<&ProvidedParam, ResolveError> {
755 self.inner
756 .iter()
757 .find(|p| p.name == name)
758 .ok_or(ResolveError::ParamNotProvided(name.to_string()))
759 }
760
761 pub fn resolve_tag_value(&self, pv: Parameterized<TagValue>) -> Result<TagValue, ResolveError> {
763 let param = match pv {
764 Parameterized::Concrete(val) => return Ok(val), Parameterized::Param { span: _, param } => param,
766 };
767
768 let provided_param = self.get_param(¶m.name)?;
769 match &provided_param.value {
770 ParamValue::String(val) => Ok(TagValue::String(SharedString::try_from(val)?)),
771 ParamValue::Int(val) => Ok(TagValue::Int(*val)),
772 ParamValue::Float(val) => Ok(TagValue::Float(*val)),
773 ParamValue::Bool(val) => Ok(TagValue::Bool(*val)),
774 val => Err(ResolveError::InvalidType {
775 name: param.name,
776 defined: val.typ(),
777 expected: vec![
778 TerminalParamType::Tag(TagType::String),
779 TerminalParamType::Tag(TagType::Int),
780 TerminalParamType::Tag(TagType::Float),
781 TerminalParamType::Tag(TagType::Bool),
782 ],
783 }),
784 }
785 }
786
787 pub fn resolve_dataset(&self, pv: Parameterized<Dataset>) -> Result<Dataset, ResolveError> {
789 let param = match pv {
790 Parameterized::Concrete(val) => return Ok(val), Parameterized::Param { span: _, param } => param,
792 };
793
794 let provided_param = self.get_param(¶m.name)?;
795 match &provided_param.value {
796 ParamValue::Dataset(dataset) => Ok(dataset.clone()),
797 val => Err(ResolveError::InvalidType {
798 name: param.name,
799 defined: val.typ(),
800 expected: vec![TerminalParamType::Dataset],
801 }),
802 }
803 }
804
805 pub fn resolve_relative_time(
807 &self,
808 pv: Parameterized<RelativeTime>,
809 ) -> Result<RelativeTime, ResolveError> {
810 let param = match pv {
811 Parameterized::Concrete(val) => return Ok(val), Parameterized::Param { span: _, param } => param,
813 };
814
815 let provided_param = self.get_param(¶m.name)?;
816 match &provided_param.value {
817 ParamValue::Duration(relative_time) => Ok(relative_time.clone()),
818 val => Err(ResolveError::InvalidType {
819 name: param.name,
820 defined: val.typ(),
821 expected: vec![TerminalParamType::Duration],
822 }),
823 }
824 }
825
826 pub fn resolve_regex(
828 &self,
829 pv: Parameterized<EncodableRegex>,
830 ) -> Result<EncodableRegex, ResolveError> {
831 let param = match pv {
832 Parameterized::Concrete(val) => return Ok(val), Parameterized::Param { span: _, param } => param,
834 };
835
836 let provided_param = self.get_param(¶m.name)?;
837 match &provided_param.value {
838 ParamValue::Regex(re) => Ok(re.clone()),
839 val => Err(ResolveError::InvalidType {
840 name: param.name,
841 defined: val.typ(),
842 expected: vec![TerminalParamType::Regex],
843 }),
844 }
845 }
846 #[must_use]
848 pub fn contains(&self, param: &str) -> bool {
849 self.get_param(param).is_ok()
850 }
851
852 #[must_use]
857 pub fn active_filter<'a>(&self, filter: &'a FilterOrIfDef) -> Option<&'a Filter> {
858 match filter {
859 FilterOrIfDef::Filter(filter) => Some(filter),
860 FilterOrIfDef::Ifdef { param, filter } if self.contains(¶m.name) => Some(filter),
861 FilterOrIfDef::Ifdef { .. } => None,
862 }
863 }
864
865 #[must_use]
867 pub fn active_filters<'a>(&self, filters: &'a [FilterOrIfDef]) -> Vec<&'a Filter> {
868 filters
869 .iter()
870 .filter_map(|filter| self.active_filter(filter))
871 .collect()
872 }
873}
874
875#[cfg_attr(feature = "wasm", tsify::declare)]
877pub type Params = Vec<ParamDeclaration>;
878
879#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
881#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
882#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
883pub enum Query {
884 Simple {
886 source: Source,
888 filters: Vec<FilterOrIfDef>,
890 aggregates: Vec<Aggregate>,
892 directives: Directives,
894 params: Params,
896 sample: Option<f64>,
898 },
899 Compute {
901 left: Box<Query>,
903 right: Box<Query>,
905 name: Metric,
907 op: ComputeFunction,
909 aggregates: Vec<Aggregate>,
911 directives: Directives,
913 params: Params,
915 },
916}
917
918impl Query {
919 #[must_use]
921 pub fn params(&self) -> &Params {
922 match self {
923 Query::Simple { params, .. } | Query::Compute { params, .. } => params,
924 }
925 }
926 #[must_use]
928 pub fn directives(&self) -> &Directives {
929 match self {
930 Query::Simple { directives, .. } | Query::Compute { directives, .. } => directives,
931 }
932 }
933}
934
935impl RelativeTime {
936 pub fn to_duration(&self) -> Result<Duration, TimeError> {
938 let v = i64::try_from(self.value).map_err(TimeError::InvalidDuration)?;
939 Ok(match self.unit {
940 TimeUnit::Millisecond => Duration::milliseconds(v),
941 TimeUnit::Second => Duration::seconds(v),
942 TimeUnit::Minute => Duration::minutes(v),
943 TimeUnit::Hour => Duration::hours(v),
944 TimeUnit::Day => Duration::days(v),
945 TimeUnit::Week => Duration::weeks(v),
946 TimeUnit::Month => Duration::days(v.saturating_mul(30)),
947 TimeUnit::Year => Duration::days(v.saturating_mul(365)),
948 })
949 }
950
951 pub fn to_resolution(&self) -> Result<Resolution, ResolutionError> {
953 match self.unit {
954 TimeUnit::Millisecond => Resolution::secs(self.value / 1000),
955 TimeUnit::Second => Resolution::secs(self.value),
956 TimeUnit::Minute => Resolution::secs(self.value.saturating_mul(60)),
957 TimeUnit::Hour => Resolution::secs(self.value.saturating_mul(60 * 60)),
958 TimeUnit::Day => Resolution::secs(self.value.saturating_mul(60 * 60 * 24)),
959 TimeUnit::Week => Resolution::secs(self.value.saturating_mul(60 * 60 * 24 * 7)),
960 TimeUnit::Month => Resolution::secs(self.value.saturating_mul(60 * 60 * 24 * 30)),
961 TimeUnit::Year => Resolution::secs(self.value.saturating_mul(60 * 60 * 24 * 365)),
962 }
963 }
964}
965
966#[derive(Debug, thiserror::Error)]
968pub enum TimeError {
969 #[error("Invalid timestamp {0}, could not be converted to a UTC datetime")]
971 InvalidTimestamp(i64),
972 #[error(
974 "Invalid duration {0}, could not be converted to Duration as it exceeds the maximum i64"
975 )]
976 InvalidDuration(TryFromIntError),
977}
978#[cfg(feature = "clock")]
979impl Time {
980 fn to_datetime(&self) -> Result<DateTime<Utc>, TimeError> {
981 Ok(match self {
982 Time::Relative(t) => Utc::now() - t.to_duration()?,
983 Time::Timestamp(ts) => {
984 DateTime::<Utc>::from_timestamp(*ts, 0).ok_or(TimeError::InvalidTimestamp(*ts))?
985 }
986 Time::RFC3339(t) => t.with_timezone(&Utc),
987 Time::Modifier(_) => todo!(),
988 })
989 }
990}
991
992#[cfg(feature = "clock")]
993impl TimeRange {
994 pub fn to_start_end(&self) -> Result<(DateTime<Utc>, DateTime<Utc>), TimeError> {
996 let start = self.start.to_datetime()?;
997 let end = self
998 .end
999 .as_ref()
1000 .map_or_else(|| Ok(Utc::now()), Time::to_datetime)?;
1001 Ok((start, end))
1002 }
1003}