protoschema 0.1.9

📐 Programmatically define protobuf contracts using flexible, modular and reusable elements
Documentation
use bon::Builder;

use crate::{
  field_type::{Duration, Timestamp},
  validators::{cel::CelRule, validate_comparables, Ignore, OptionValueList},
  OptionValue, ProtoOption,
};

/// Used by the [`timestamp`](crate::timestamp) macro to define validation rules.
#[derive(Clone, Debug, Builder)]
pub struct TimestampValidator {
  /// Only this specific value will be considered valid for this field.
  pub const_: Option<Timestamp>,
  /// This field's value will be valid only if it is smaller than the specified amount.
  pub lt: Option<Timestamp>,
  /// This field's value will be valid only if it is smaller than, or equal to, the specified amount.
  pub lte: Option<Timestamp>,
  #[builder(with = || true)]
  /// This field's value will be valid only if it in the past.
  pub lt_now: Option<bool>,
  /// This field's value will be valid only if it is greater than the specified amount.
  pub gt: Option<Timestamp>,
  /// This field's value will be valid only if it is greater than, or equal to, the specified amount.
  pub gte: Option<Timestamp>,
  #[builder(with = || true)]
  /// This field's value will be valid only if it in the future.
  pub gt_now: Option<bool>,
  /// This field's value will be valid only if it is within the specified Duration (either in the past or future) from the moment when it's being validated.
  pub within: Option<Duration>,
  /// Adds custom validation using one or more [`CelRule`]s to this field.
  #[builder(into)]
  pub cel: Option<Box<[CelRule]>>,
  #[builder(with = || true)]
  /// Marks the field as invalid if unset.
  pub required: Option<bool>,
  #[builder(setters(vis = "", name = ignore))]
  pub ignore: Option<Ignore>,
}

impl_ignore!(no_lifetime, TimestampValidatorBuilder);

impl<S: timestamp_validator_builder::State> From<TimestampValidatorBuilder<S>> for ProtoOption {
  #[track_caller]
  fn from(value: TimestampValidatorBuilder<S>) -> Self {
    value.build().into()
  }
}

impl From<TimestampValidator> for ProtoOption {
  #[track_caller]
  fn from(validator: TimestampValidator) -> Self {
    let name = "(buf.validate.field)";

    let mut values: OptionValueList = Vec::new();

    if let Some(const_val) = validator.const_ {
      values.push(("const".into(), OptionValue::Timestamp(const_val)));
    }

    validate_comparables(validator.lt, validator.lte, validator.gt, validator.gte);

    if let Some(true) = validator.lt_now {
      if validator.lt.is_some() || validator.lte.is_some() {
        panic!("Cannot use lt_now with lt or lte")
      }

      if let Some(gt) = validator.gt && gt.is_future() {
      panic!("Gt cannot be in the future if lt_now is true")
    }
      if let Some(gte) = validator.gte && gte.is_future() {
      panic!("Gte cannot be in the future if lt_now is true")
    }
    }

    if let Some(true) = validator.gt_now {
      if validator.gt.is_some() || validator.gte.is_some() {
        panic!("Cannot use gt_now with gt or gte")
      }

      if let Some(lt) = validator.lt && lt.is_past() {
      panic!("Lt cannot be in the past if gt_now is true")
    }
      if let Some(lte) = validator.lte && lte.is_past() {
      panic!("Lte cannot be in the past if gt_now is true")
    }
    }

    insert_option!(validator, values, lt, timestamp);
    insert_option!(validator, values, lte, timestamp);
    insert_option!(validator, values, gt, timestamp);
    insert_option!(validator, values, gte, timestamp);
    insert_option!(validator, values, lt_now, bool);
    insert_option!(validator, values, gt_now, bool);
    insert_option!(validator, values, within, duration);

    let mut option_value: OptionValueList = vec![(
      "timestamp".into(),
      OptionValue::Message(values.into_boxed_slice()),
    )];

    insert_cel_rule!(validator, option_value);
    insert_option!(validator, option_value, required, bool);

    ProtoOption {
      name,
      value: OptionValue::Message(option_value.into_boxed_slice()).into(),
    }
  }
}

#[doc(hidden)]
#[track_caller]
pub fn build_timestamp_validator_option<F, S>(config_fn: F) -> ProtoOption
where
  F: FnOnce(TimestampValidatorBuilder) -> TimestampValidatorBuilder<S>,
  S: timestamp_validator_builder::IsComplete,
{
  let builder = TimestampValidator::builder();
  let validator = config_fn(builder).build();
  validator.into()
}