use crate::{sys, Instant, Timeline};
use std::fmt::Debug;
pub trait State {
fn add_options(&self, options: &mut u64);
}
pub trait ValueState: State {
type ReferenceTimeline: Timeline;
type OutputTimeline: Timeline;
fn reference_value(&self) -> Option<Instant<Self::ReferenceTimeline>>;
fn synthetic_value(&self) -> Option<Instant<Self::OutputTimeline>>;
}
pub trait RateState: State {
fn rate_adjustment(&self) -> Option<i32>;
}
pub trait ErrorState: State {
fn error_bound(&self) -> Option<u64>;
}
pub struct Null<R, O>(std::marker::PhantomData<(R, O)>);
impl<R: Timeline, O: Timeline> State for Null<R, O> {
fn add_options(&self, _: &mut u64) {}
}
impl<R: Timeline, O: Timeline> ValueState for Null<R, O> {
type ReferenceTimeline = R;
type OutputTimeline = O;
fn reference_value(&self) -> Option<Instant<R>> {
None
}
fn synthetic_value(&self) -> Option<Instant<O>> {
None
}
}
impl<R: Timeline, O: Timeline> RateState for Null<R, O> {
fn rate_adjustment(&self) -> Option<i32> {
None
}
}
impl<R: Timeline, O: Timeline> ErrorState for Null<R, O> {
fn error_bound(&self) -> Option<u64> {
None
}
}
pub struct AbsoluteValue<R, O> {
reference_value: Instant<R>,
synthetic_value: Instant<O>,
}
impl<R: Timeline + Copy, O: Timeline + Copy> State for AbsoluteValue<R, O> {
#[inline]
fn add_options(&self, opts: &mut u64) {
*opts |= sys::ZX_CLOCK_UPDATE_OPTION_REFERENCE_VALUE_VALID
| sys::ZX_CLOCK_UPDATE_OPTION_SYNTHETIC_VALUE_VALID;
}
}
impl<R: Timeline + Copy, O: Timeline + Copy> ValueState for AbsoluteValue<R, O> {
type ReferenceTimeline = R;
type OutputTimeline = O;
fn reference_value(&self) -> Option<Instant<R>> {
Some(self.reference_value)
}
fn synthetic_value(&self) -> Option<Instant<O>> {
Some(self.synthetic_value)
}
}
pub struct ApproximateValue<R, O>(Instant<O>, std::marker::PhantomData<R>);
impl<R: Timeline + Copy, O: Timeline + Copy> State for ApproximateValue<R, O> {
#[inline]
fn add_options(&self, opts: &mut u64) {
*opts |= sys::ZX_CLOCK_UPDATE_OPTION_SYNTHETIC_VALUE_VALID;
}
}
impl<R: Timeline + Copy, O: Timeline + Copy> ValueState for ApproximateValue<R, O> {
type ReferenceTimeline = R;
type OutputTimeline = O;
fn reference_value(&self) -> Option<Instant<R>> {
None
}
fn synthetic_value(&self) -> Option<Instant<O>> {
Some(self.0)
}
}
pub struct Rate(i32);
impl State for Rate {
#[inline]
fn add_options(&self, opts: &mut u64) {
*opts |= sys::ZX_CLOCK_UPDATE_OPTION_RATE_ADJUST_VALID;
}
}
impl RateState for Rate {
fn rate_adjustment(&self) -> Option<i32> {
Some(self.0)
}
}
pub struct Error(u64);
impl State for Error {
#[inline]
fn add_options(&self, opts: &mut u64) {
*opts |= sys::ZX_CLOCK_UPDATE_OPTION_ERROR_BOUND_VALID;
}
}
impl ErrorState for Error {
fn error_bound(&self) -> Option<u64> {
Some(self.0)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct ClockUpdateBuilder<Val, Rate, Err, Ref, Out> {
value_state: Val,
rate_state: Rate,
error_state: Err,
_output_marker: std::marker::PhantomData<(Ref, Out)>,
}
impl<
Val: ValueState<ReferenceTimeline = Ref, OutputTimeline = Out>,
Rate: RateState,
Err: ErrorState,
Ref: Timeline,
Out: Timeline,
> ClockUpdateBuilder<Val, Rate, Err, Ref, Out>
{
#[inline]
pub fn build(self) -> ClockUpdate<Ref, Out> {
ClockUpdate::from(self)
}
}
impl<R: Timeline, O: Timeline> ClockUpdateBuilder<Null<R, O>, Null<R, O>, Null<R, O>, R, O> {
#[inline]
fn new() -> Self {
Self {
value_state: Null(std::marker::PhantomData),
rate_state: Null(std::marker::PhantomData),
error_state: Null(std::marker::PhantomData),
_output_marker: std::marker::PhantomData,
}
}
}
impl<Rate: RateState, Err: ErrorState, Ref: Timeline, Out: Timeline>
ClockUpdateBuilder<Null<Ref, Out>, Rate, Err, Ref, Out>
{
#[inline]
pub fn absolute_value(
self,
reference_value: Instant<Ref>,
synthetic_value: Instant<Out>,
) -> ClockUpdateBuilder<AbsoluteValue<Ref, Out>, Rate, Err, Ref, Out> {
ClockUpdateBuilder {
value_state: AbsoluteValue { reference_value, synthetic_value },
rate_state: self.rate_state,
error_state: self.error_state,
_output_marker: std::marker::PhantomData,
}
}
}
impl<Err: ErrorState, Ref: Timeline, Out: Timeline>
ClockUpdateBuilder<Null<Ref, Out>, Null<Ref, Out>, Err, Ref, Out>
{
#[inline]
pub fn approximate_value(
self,
synthetic_value: Instant<Out>,
) -> ClockUpdateBuilder<ApproximateValue<Ref, Out>, Null<Ref, Out>, Err, Ref, Out> {
ClockUpdateBuilder {
value_state: ApproximateValue(synthetic_value, std::marker::PhantomData),
rate_state: self.rate_state,
error_state: self.error_state,
_output_marker: std::marker::PhantomData,
}
}
}
impl<Err: ErrorState, Ref: Timeline, Out: Timeline>
ClockUpdateBuilder<Null<Ref, Out>, Null<Ref, Out>, Err, Ref, Out>
{
#[inline]
pub fn rate_adjust(
self,
rate_adjust_ppm: i32,
) -> ClockUpdateBuilder<Null<Ref, Out>, Rate, Err, Ref, Out> {
ClockUpdateBuilder {
value_state: self.value_state,
rate_state: Rate(rate_adjust_ppm),
error_state: self.error_state,
_output_marker: std::marker::PhantomData,
}
}
}
impl<Err: ErrorState, Ref: Timeline, Out: Timeline>
ClockUpdateBuilder<AbsoluteValue<Ref, Out>, Null<Ref, Out>, Err, Ref, Out>
{
#[inline]
pub fn rate_adjust(
self,
rate_adjust_ppm: i32,
) -> ClockUpdateBuilder<AbsoluteValue<Ref, Out>, Rate, Err, Ref, Out> {
ClockUpdateBuilder {
value_state: self.value_state,
rate_state: Rate(rate_adjust_ppm),
error_state: self.error_state,
_output_marker: std::marker::PhantomData,
}
}
}
impl<Val: ValueState, Rate: RateState, Ref: Timeline, Out: Timeline>
ClockUpdateBuilder<Val, Rate, Null<Ref, Out>, Ref, Out>
{
#[inline]
pub fn error_bounds(
self,
error_bound_ns: u64,
) -> ClockUpdateBuilder<Val, Rate, Error, Ref, Out> {
ClockUpdateBuilder {
value_state: self.value_state,
rate_state: self.rate_state,
error_state: Error(error_bound_ns),
_output_marker: std::marker::PhantomData,
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct ClockUpdate<Reference, Output> {
options: u64,
rate_adjust: i32,
synthetic_value: Instant<Output>,
reference_value: Instant<Reference>,
error_bound: u64,
}
impl<R: Timeline, O: Timeline> ClockUpdate<R, O> {
#[inline]
pub fn builder() -> ClockUpdateBuilder<Null<R, O>, Null<R, O>, Null<R, O>, R, O> {
ClockUpdateBuilder::new()
}
#[inline]
pub fn options(&self) -> u64 {
self.options
}
pub(crate) fn args(self) -> sys::zx_clock_update_args_v2_t {
let mut ret = sys::zx_clock_update_args_v2_t::default();
ret.rate_adjust = self.rate_adjust;
ret.synthetic_value = self.synthetic_value.into_nanos();
ret.reference_value = self.reference_value.into_nanos();
ret.error_bound = self.error_bound;
ret
}
}
impl<
Val: ValueState<ReferenceTimeline = Ref, OutputTimeline = Out>,
Rate: RateState,
Err: ErrorState,
Ref: Timeline,
Out: Timeline,
> From<ClockUpdateBuilder<Val, Rate, Err, Ref, Out>> for ClockUpdate<Ref, Out>
{
fn from(builder: ClockUpdateBuilder<Val, Rate, Err, Ref, Out>) -> Self {
let mut options = sys::ZX_CLOCK_ARGS_VERSION_2;
builder.value_state.add_options(&mut options);
builder.rate_state.add_options(&mut options);
builder.error_state.add_options(&mut options);
Self {
options,
rate_adjust: builder.rate_state.rate_adjustment().unwrap_or_default(),
synthetic_value: builder.value_state.synthetic_value().unwrap_or_default(),
reference_value: builder.value_state.reference_value().unwrap_or_default(),
error_bound: builder.error_state.error_bound().unwrap_or_default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{MonotonicInstant, MonotonicTimeline, SyntheticInstant, SyntheticTimeline};
#[test]
fn empty_update() {
let update =
ClockUpdateBuilder::<_, _, _, MonotonicTimeline, SyntheticTimeline>::new().build();
assert_eq!(update.options(), sys::ZX_CLOCK_ARGS_VERSION_2);
assert_eq!(update.args(), sys::zx_clock_update_args_v2_t::default(),);
}
#[test]
fn rate_only() {
let update = ClockUpdate::<MonotonicTimeline, SyntheticTimeline>::from(
ClockUpdateBuilder::new().rate_adjust(52),
);
assert_eq!(
update.options(),
sys::ZX_CLOCK_ARGS_VERSION_2 | sys::ZX_CLOCK_UPDATE_OPTION_RATE_ADJUST_VALID
);
let args = update.args();
assert_eq!(args.rate_adjust, 52);
assert_eq!(args.reference_value, 0);
assert_eq!(args.synthetic_value, 0);
assert_eq!(args.error_bound, 0);
}
#[test]
fn approximate_value() {
let update = ClockUpdateBuilder::<_, _, _, MonotonicTimeline, SyntheticTimeline>::new()
.approximate_value(SyntheticInstant::from_nanos(42))
.error_bounds(62)
.build();
assert_eq!(
update.options(),
sys::ZX_CLOCK_ARGS_VERSION_2
| sys::ZX_CLOCK_UPDATE_OPTION_SYNTHETIC_VALUE_VALID
| sys::ZX_CLOCK_UPDATE_OPTION_ERROR_BOUND_VALID
);
let args = update.args();
assert_eq!(args.rate_adjust, 0);
assert_eq!(args.reference_value, 0);
assert_eq!(args.synthetic_value, 42);
assert_eq!(args.error_bound, 62);
}
#[test]
fn absolute_value() {
let update = ClockUpdateBuilder::<_, _, _, MonotonicTimeline, SyntheticTimeline>::new()
.absolute_value(MonotonicInstant::from_nanos(1000), SyntheticInstant::from_nanos(42))
.rate_adjust(52)
.error_bounds(62)
.build();
assert_eq!(
update.options(),
sys::ZX_CLOCK_ARGS_VERSION_2
| sys::ZX_CLOCK_UPDATE_OPTION_REFERENCE_VALUE_VALID
| sys::ZX_CLOCK_UPDATE_OPTION_SYNTHETIC_VALUE_VALID
| sys::ZX_CLOCK_UPDATE_OPTION_RATE_ADJUST_VALID
| sys::ZX_CLOCK_UPDATE_OPTION_ERROR_BOUND_VALID
);
let args = update.args();
assert_eq!(args.rate_adjust, 52);
assert_eq!(args.reference_value, 1000);
assert_eq!(args.synthetic_value, 42);
assert_eq!(args.error_bound, 62);
}
}