From 66c535e4d67b2ebcb726eaad4cb6daf24c650ecc Mon Sep 17 00:00:00 2001 From: philippeitis <33013301+philippeitis@users.noreply.github.com> Date: Fri, 7 Oct 2022 02:14:26 -0700 Subject: [PATCH] Add support for duration and base64 serde --- google-apis-common/Cargo.toml | 3 +- google-apis-common/src/lib.rs | 97 +++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/google-apis-common/Cargo.toml b/google-apis-common/Cargo.toml index 2edda545ef..c0cc13475a 100644 --- a/google-apis-common/Cargo.toml +++ b/google-apis-common/Cargo.toml @@ -17,7 +17,8 @@ doctest = false [dependencies] mime = "^ 0.2.0" -serde = "^ 1.0" +serde = { version = "^ 1.0", features = ["derive"] } +base64 = "0.13.0" serde_json = "^ 1.0" ## TODO: Make yup-oauth2 optional ## yup-oauth2 = { version = "^ 7.0", optional = true } diff --git a/google-apis-common/src/lib.rs b/google-apis-common/src/lib.rs index 0b1900f07f..37eb28e56c 100644 --- a/google-apis-common/src/lib.rs +++ b/google-apis-common/src/lib.rs @@ -843,6 +843,103 @@ mod yup_oauth2_impl { } } +pub mod types { + use std::str::FromStr; + use serde::{Deserialize, Deserializer, Serializer}; + // https://github.com/protocolbuffers/protobuf-go/blob/6875c3d7242d1a3db910ce8a504f124cb840c23a/types/known/durationpb/duration.pb.go#L148 + #[derive(Deserialize)] + #[serde(try_from = "IntermediateDuration")] + pub struct Duration { + pub seconds: i64, + pub nanoseconds: i32, + } + + #[derive(Deserialize)] + struct IntermediateDuration<'a>(&'a str); + + impl serde::Serialize for Duration { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + if self.nanoseconds != 0 { + if self.seconds == 0 && self.nanoseconds.is_negative() { + serializer.serialize_str(&format!("-0.{}s", self.nanoseconds.abs())) + } else { + serializer.serialize_str(&format!("{}.{}s", self.seconds, self.nanoseconds.abs())) + } + } else { + serializer.serialize_str(&format!("{}s", self.seconds)) + } + } + } + + impl <'a> TryFrom> for Duration { + type Error = std::num::ParseIntError; + + fn try_from(value: IntermediateDuration<'a>) -> Result { + let abs_duration = 315576000000i64; + // TODO: Test strings like -.s, -0.0s + if !value.0.ends_with('s') { + todo!(); + } + let (seconds, nanoseconds) = if let Some((seconds, nanos)) = value.0.split_once('.') { + let seconds = i64::from_str(seconds)?; + let nano_len = nanos.len() - 1; + // up . 000_000_000 + let nano_digits = nanos.chars().filter(|c| c.is_digit(10)).count() as u32; + if nano_digits > 9 { + todo!() + } + // 2 digits: 120_000_000 + // 9 digits: 123_456_789 + // pad to 9 digits + let nanos = i32::from_str(&nanos[..nanos.len() - 1])? * 10_i32.pow(9 - nano_digits); + (seconds, nanos) + } else { + // TODO: handle negative number + (i64::from_str(&value.0[..value.0.len().saturating_sub(1)])?, 0) + }; + if (seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0) { + todo!(); + } + + if seconds >= abs_duration || seconds <= -abs_duration { + todo!(); + } + if nanoseconds >= 1_000_000_000 || nanoseconds <= -1_000_000_000 { + todo!(); + } + + Ok(Duration { seconds, nanoseconds }) + } + } + + + // #[serde(serialize_with = "path")] + fn to_urlsafe_base64(x: &str, s: S) -> Result + where + S: Serializer, + { + s.serialize_str(&base64::encode_config(x, base64::URL_SAFE)) + } + // #[serde(deserialize_with = "path")] + fn from_urlsafe_base64<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s: &str = Deserialize::deserialize(deserializer)?; + Ok(base64::decode_config(s, base64::URL_SAFE).unwrap()) + } + // TODO: + // "google-datetime", + // "date-time", + // "date", + + // TODO: https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/field-mask + // "google-fieldmask", +} + #[cfg(test)] mod test_api { use super::*;