datetime_string/rfc3339/offset/
owned.rs1#![cfg(feature = "alloc")]
5
6use core::{convert::TryFrom, fmt, ops, str};
7
8use alloc::{borrow::ToOwned, string::String, vec::Vec};
9
10use crate::{
11 common::{TimeNumOffsetColonStr, TimeNumOffsetColonString, TimeOffsetSign},
12 error::{ConversionError, Error},
13};
14
15use super::{validate_bytes, TimeOffsetStr};
16
17#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
40#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
41#[repr(transparent)]
42#[allow(clippy::derive_hash_xor_eq)]
49#[allow(unknown_lints, clippy::derive_ord_xor_partial_ord)]
50pub struct TimeOffsetString(Vec<u8>);
51
52impl TimeOffsetString {
53 #[inline]
61 #[must_use]
62 unsafe fn from_bytes_maybe_unchecked(s: Vec<u8>) -> Self {
63 debug_assert_ok!(validate_bytes(&s));
64 Self(s)
65 }
66
67 #[inline]
79 #[must_use]
80 pub fn z() -> Self {
81 let z = *b"Z";
82 unsafe {
83 debug_assert_ok!(validate_bytes(&z));
85 Self::from_bytes_maybe_unchecked(z[..].to_owned())
86 }
87 }
88
89 #[inline]
100 #[must_use]
101 pub fn unknown_local_offset() -> Self {
102 TimeNumOffsetColonString::unknown_local_offset().into()
103 }
104
105 pub fn from_minutes(minutes: i16) -> Result<Self, Error> {
118 TimeNumOffsetColonString::from_minutes(minutes).map(Into::into)
119 }
120
121 pub fn from_sign_and_hm(sign: TimeOffsetSign, hour_abs: u8, minute: u8) -> Result<Self, Error> {
141 TimeNumOffsetColonString::from_sign_and_hm(sign, hour_abs, minute).map(Into::into)
142 }
143
144 pub fn from_hm_signed(hour: i8, minute: u8) -> Result<Self, Error> {
160 TimeNumOffsetColonString::from_hm_signed(hour, minute).map(Into::into)
161 }
162
163 #[inline]
179 #[must_use]
180 pub fn as_deref(&self) -> &TimeOffsetStr {
181 unsafe {
182 debug_assert_safe_version_ok!(TimeOffsetStr::from_bytes(&self.0));
184 TimeOffsetStr::from_bytes_maybe_unchecked(&self.0)
185 }
186 }
187
188 #[inline]
204 #[must_use]
205 pub fn as_deref_mut(&mut self) -> &mut TimeOffsetStr {
206 unsafe {
207 debug_assert_ok!(TimeOffsetStr::from_bytes(&self.0));
211 TimeOffsetStr::from_bytes_maybe_unchecked_mut(&mut self.0)
212 }
213 }
214}
215
216impl core::borrow::Borrow<TimeOffsetStr> for TimeOffsetString {
217 #[inline]
218 fn borrow(&self) -> &TimeOffsetStr {
219 self.as_deref()
220 }
221}
222
223impl core::borrow::BorrowMut<TimeOffsetStr> for TimeOffsetString {
224 #[inline]
225 fn borrow_mut(&mut self) -> &mut TimeOffsetStr {
226 self.as_deref_mut()
227 }
228}
229
230impl alloc::borrow::ToOwned for TimeOffsetStr {
231 type Owned = TimeOffsetString;
232
233 #[inline]
234 fn to_owned(&self) -> Self::Owned {
235 self.into()
236 }
237}
238
239impl AsRef<[u8]> for TimeOffsetString {
240 #[inline]
241 fn as_ref(&self) -> &[u8] {
242 self.as_bytes()
243 }
244}
245
246impl AsRef<str> for TimeOffsetString {
247 #[inline]
248 fn as_ref(&self) -> &str {
249 self.as_str()
250 }
251}
252
253impl AsRef<TimeOffsetStr> for TimeOffsetString {
254 #[inline]
255 fn as_ref(&self) -> &TimeOffsetStr {
256 self
257 }
258}
259
260impl AsMut<TimeOffsetStr> for TimeOffsetString {
261 #[inline]
262 fn as_mut(&mut self) -> &mut TimeOffsetStr {
263 self
264 }
265}
266
267impl From<&TimeNumOffsetColonStr> for TimeOffsetString {
268 #[inline]
269 fn from(v: &TimeNumOffsetColonStr) -> TimeOffsetString {
270 unsafe {
271 debug_assert_ok!(validate_bytes(v.as_bytes()));
273 Self::from_bytes_maybe_unchecked(v.as_bytes().to_owned())
274 }
275 }
276}
277
278impl From<TimeNumOffsetColonString> for TimeOffsetString {
279 #[inline]
280 fn from(v: TimeNumOffsetColonString) -> TimeOffsetString {
281 unsafe {
282 debug_assert_ok!(validate_bytes(v.as_bytes()));
284 Self::from_bytes_maybe_unchecked(v.as_bytes().to_owned())
285 }
286 }
287}
288
289impl From<TimeOffsetString> for Vec<u8> {
290 #[inline]
291 fn from(v: TimeOffsetString) -> Vec<u8> {
292 v.0
293 }
294}
295
296impl From<TimeOffsetString> for String {
297 #[inline]
298 fn from(v: TimeOffsetString) -> String {
299 unsafe {
300 debug_assert_ok!(str::from_utf8(&v.0));
302 String::from_utf8_unchecked(v.0)
303 }
304 }
305}
306
307impl From<&TimeOffsetStr> for TimeOffsetString {
308 fn from(v: &TimeOffsetStr) -> Self {
309 unsafe {
310 debug_assert_ok!(validate_bytes(&v.0));
312 Self::from_bytes_maybe_unchecked(v.0.into())
313 }
314 }
315}
316
317impl TryFrom<&[u8]> for TimeOffsetString {
318 type Error = Error;
319
320 #[inline]
321 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
322 TimeOffsetStr::from_bytes(v).map(Into::into)
323 }
324}
325
326impl TryFrom<&str> for TimeOffsetString {
327 type Error = Error;
328
329 #[inline]
330 fn try_from(v: &str) -> Result<Self, Self::Error> {
331 TimeOffsetStr::from_str(v).map(Into::into)
332 }
333}
334
335impl TryFrom<Vec<u8>> for TimeOffsetString {
336 type Error = ConversionError<Vec<u8>>;
337
338 #[inline]
339 fn try_from(v: Vec<u8>) -> Result<Self, Self::Error> {
340 match validate_bytes(&v) {
341 Ok(_) => Ok(unsafe {
342 Self::from_bytes_maybe_unchecked(v)
344 }),
345 Err(e) => Err(ConversionError::new(v, e)),
346 }
347 }
348}
349
350impl TryFrom<String> for TimeOffsetString {
351 type Error = ConversionError<String>;
352
353 #[inline]
354 fn try_from(v: String) -> Result<Self, Self::Error> {
355 match validate_bytes(v.as_bytes()) {
356 Ok(_) => Ok(unsafe {
357 Self::from_bytes_maybe_unchecked(v.into_bytes())
359 }),
360 Err(e) => Err(ConversionError::new(v, e)),
361 }
362 }
363}
364
365impl fmt::Display for TimeOffsetString {
366 #[inline]
367 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368 self.as_deref().fmt(f)
369 }
370}
371
372impl ops::Deref for TimeOffsetString {
373 type Target = TimeOffsetStr;
374
375 #[inline]
376 fn deref(&self) -> &Self::Target {
377 self.as_deref()
378 }
379}
380
381impl ops::DerefMut for TimeOffsetString {
382 #[inline]
383 fn deref_mut(&mut self) -> &mut Self::Target {
384 self.as_deref_mut()
385 }
386}
387
388impl str::FromStr for TimeOffsetString {
389 type Err = Error;
390
391 #[inline]
392 fn from_str(s: &str) -> Result<Self, Self::Err> {
393 Self::try_from(s)
394 }
395}
396
397impl_cmp_symmetric!(TimeOffsetStr, TimeOffsetString, &TimeOffsetString);
398impl_cmp_symmetric!(TimeOffsetStr, TimeOffsetString, TimeOffsetStr);
399impl_cmp_symmetric!(TimeOffsetStr, TimeOffsetString, &TimeOffsetStr);
400impl_cmp_symmetric!(str, TimeOffsetString, str);
401impl_cmp_symmetric!(str, TimeOffsetString, &str);
402impl_cmp_symmetric!(str, &TimeOffsetString, str);
403impl_cmp_symmetric!([u8], TimeOffsetString, [u8]);
404impl_cmp_symmetric!([u8], TimeOffsetString, &[u8]);
405impl_cmp_symmetric!([u8], &TimeOffsetString, [u8]);
406
407#[cfg(feature = "serde")]
408#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
409impl serde::Serialize for TimeOffsetString {
410 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
411 where
412 S: serde::Serializer,
413 {
414 serializer.serialize_str(self.as_str())
415 }
416}
417
418#[cfg(feature = "serde")]
420mod serde_ {
421 use super::*;
422
423 use serde::de::{Deserialize, Deserializer, Visitor};
424
425 struct StringVisitor;
427
428 impl<'de> Visitor<'de> for StringVisitor {
429 type Value = TimeOffsetString;
430
431 #[inline]
432 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
433 f.write_str("RFC 3339 time-offset string")
434 }
435
436 #[inline]
437 fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
438 where
439 E: serde::de::Error,
440 {
441 Self::Value::try_from(v).map_err(E::custom)
442 }
443
444 #[inline]
445 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
446 where
447 E: serde::de::Error,
448 {
449 Self::Value::try_from(v).map_err(E::custom)
450 }
451 }
452
453 impl<'de> Deserialize<'de> for TimeOffsetString {
454 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
455 where
456 D: Deserializer<'de>,
457 {
458 deserializer.deserialize_any(StringVisitor)
459 }
460 }
461}
462
463#[cfg(feature = "serde")]
464#[cfg(test)]
465mod tests {
466 use super::*;
467
468 use serde_test::{assert_de_tokens, assert_tokens, Token};
469
470 #[test]
471 fn ser_de_string() {
472 let raw: &'static str = "-12:34";
473 assert_tokens(
474 &TimeOffsetString::try_from(raw).unwrap(),
475 &[Token::Str(raw)],
476 );
477 }
478
479 #[test]
480 fn de_bytes() {
481 let raw: &'static [u8; 6] = b"-12:34";
482 assert_de_tokens(
483 &TimeOffsetString::try_from(&raw[..]).unwrap(),
484 &[Token::Bytes(raw)],
485 );
486 }
487}