diff --git a/protobuf/src/cached_size.rs b/protobuf/src/cached_size.rs index c515f0942..706269f5b 100644 --- a/protobuf/src/cached_size.rs +++ b/protobuf/src/cached_size.rs @@ -15,6 +15,11 @@ pub struct CachedSize { } impl CachedSize { + /// Default value for cached size. + pub const INIT: CachedSize = CachedSize { + size: AtomicUsize::new(0), + }; + /// Get cached size pub fn get(&self) -> u32 { self.size.load(Ordering::Relaxed) as u32 diff --git a/protobuf/src/lib.rs b/protobuf/src/lib.rs index 8b5a291a3..cb890a235 100644 --- a/protobuf/src/lib.rs +++ b/protobuf/src/lib.rs @@ -48,6 +48,7 @@ pub mod plugin; pub mod rustproto; mod any; +mod timestamp; mod clear; mod core; diff --git a/protobuf/src/timestamp.rs b/protobuf/src/timestamp.rs new file mode 100644 index 000000000..5b7636d0f --- /dev/null +++ b/protobuf/src/timestamp.rs @@ -0,0 +1,112 @@ +use crate::cached_size::CachedSize; +use crate::well_known_types::Timestamp; +use crate::UnknownFields; +use std::time::{Duration, SystemTime}; + +impl Timestamp { + /// Unix epoch value of timestamp. + pub const UNIX_EPOCH: Timestamp = Timestamp { + seconds: 0, + nanos: 0, + unknown_fields: UnknownFields::INIT, + cached_size: CachedSize::INIT, + }; +} + +/// Convert from [`Timestamp`]. +/// +/// # Panics +/// +/// This function panics if given `SystemTime` is outside of `Timestamp` range. +impl From for Timestamp { + fn from(time: SystemTime) -> Self { + match time.duration_since(SystemTime::UNIX_EPOCH) { + Ok(since_epoch) => Timestamp { + seconds: since_epoch.as_secs() as i64, + nanos: since_epoch.subsec_nanos() as i32, + ..Default::default() + }, + Err(e) => { + let before_epoch = e.duration(); + Timestamp { + seconds: -(before_epoch.as_secs() as i64) + - (before_epoch.subsec_nanos() != 0) as i64, + nanos: (1_000_000_000 - before_epoch.subsec_nanos() as i32) % 1_000_000_000, + ..Default::default() + } + } + } + } +} + +/// Convert into [`SystemTime`]. +/// +/// The conversion could be lossy if `SystemTime` precision is smaller than nanoseconds. +/// +/// # Panics +/// +/// This function panics: +/// * if given `Timestamp` is outside of `SystemTime` range +/// * if `Timestamp` is malformed +impl Into for Timestamp { + fn into(self) -> SystemTime { + if self.seconds >= 0 { + let duration = + Duration::from_secs(self.seconds as u64) + Duration::from_nanos(self.nanos as u64); + SystemTime::UNIX_EPOCH + duration + } else { + let duration = + Duration::from_secs(-self.seconds as u64) - Duration::from_nanos(self.nanos as u64); + SystemTime::UNIX_EPOCH - duration + } + } +} + +#[cfg(test)] +mod test { + use crate::well_known_types::Timestamp; + use std::time::Duration; + use std::time::SystemTime; + + #[test] + fn from_system_time() { + fn to_from(timestamp: Timestamp, system_time: SystemTime) { + assert_eq!(timestamp, Timestamp::from(system_time)); + assert_eq!(system_time, Into::::into(timestamp)); + } + + to_from(Timestamp::UNIX_EPOCH, SystemTime::UNIX_EPOCH); + to_from( + Timestamp { + seconds: 0, + nanos: 200_000_000, + ..Default::default() + }, + SystemTime::UNIX_EPOCH + Duration::from_millis(200), + ); + to_from( + Timestamp { + seconds: 3, + nanos: 200_000_000, + ..Default::default() + }, + SystemTime::UNIX_EPOCH + Duration::from_millis(3_200), + ); + to_from( + Timestamp { + seconds: -1, + nanos: 800_000_000, + ..Default::default() + }, + SystemTime::UNIX_EPOCH - Duration::from_millis(200), + ); + to_from( + Timestamp { + seconds: -4, + nanos: 800_000_000, + ..Default::default() + }, + SystemTime::UNIX_EPOCH - Duration::from_millis(3_200), + ); + } +} diff --git a/protobuf/src/unknown.rs b/protobuf/src/unknown.rs index 1b16737b6..4a0d892ff 100644 --- a/protobuf/src/unknown.rs +++ b/protobuf/src/unknown.rs @@ -176,6 +176,11 @@ pub struct UnknownFields { fields: Option>>, } +impl UnknownFields { + /// Field initializer. + pub const INIT: UnknownFields = UnknownFields { fields: None }; +} + /// Very simple hash implementation of `Hash` for `UnknownFields`. /// Since map is unordered, we cannot put entry hashes into hasher, /// instead we summing hashes of entries.