Skip to content

Commit 7703fee

Browse files
Rust 1748 Add serde_conv macro for object_id::AsHexString and object_id::FromHexString (#566)
1 parent 92aebb6 commit 7703fee

File tree

2 files changed

+111
-79
lines changed

2 files changed

+111
-79
lines changed

src/macros.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,3 +432,61 @@ macro_rules! rawdoc {
432432
object
433433
}};
434434
}
435+
436+
/// Like [`serde_with::serde_conv!`], but with additional functionality:
437+
/// 1. Supports attaching documentation (`///`) and other attributes to the generated struct
438+
/// 2. Allows serializers that return a [`Result`]`, enabling error handling during serialization
439+
///
440+
/// This macro generates a `SerializeAs`/`DeserializeAs` implementation for a given type,
441+
/// with optional struct-level attributes like `#[derive(...)]` or `/// doc comments`.
442+
macro_rules! serde_conv_doc {
443+
($(#[$meta:meta])* $vis:vis $m:ident, $t:ty, $ser:expr, $de:expr) => {
444+
#[allow(non_camel_case_types)]
445+
$(#[$meta])*
446+
$vis struct $m;
447+
448+
// Prevent clippy lints triggering because of the template here
449+
// https://github.com/jonasbb/serde_with/pull/320
450+
// https://github.com/jonasbb/serde_with/pull/729
451+
#[allow(clippy::all)]
452+
const _:() = {
453+
impl $m {
454+
$vis fn serialize<S>(x: &$t, serializer: S) -> Result<S::Ok, S::Error>
455+
where
456+
S: Serializer,
457+
{
458+
let y = $ser(x).map_err(serde::ser::Error::custom)?;
459+
Serialize::serialize(&y, serializer)
460+
}
461+
462+
$vis fn deserialize<'de, D>(deserializer: D) -> Result<$t, D::Error>
463+
where
464+
D: Deserializer<'de>,
465+
{
466+
let y = Deserialize::deserialize(deserializer)?;
467+
$de(y).map_err(serde::de::Error::custom)
468+
}
469+
}
470+
471+
impl SerializeAs<$t> for $m {
472+
fn serialize_as<S>(x: &$t, serializer: S) -> Result<S::Ok, S::Error>
473+
where
474+
S: Serializer,
475+
{
476+
Self::serialize(x, serializer)
477+
}
478+
}
479+
480+
impl<'de> DeserializeAs<'de, $t> for $m {
481+
fn deserialize_as<D>(deserializer: D) -> Result<$t, D::Error>
482+
where
483+
D: Deserializer<'de>,
484+
{
485+
Self::deserialize(deserializer)
486+
}
487+
}
488+
};
489+
};
490+
}
491+
492+
pub(crate) use serde_conv_doc;

src/serde_helpers.rs

Lines changed: 53 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -91,90 +91,64 @@ pub fn serialize_u64_as_i64<S: Serializer>(val: &u64, serializer: S) -> Result<S
9191

9292
#[cfg(feature = "serde_with-3")]
9393
pub mod object_id {
94-
use crate::oid::ObjectId;
95-
use serde::{ser, Deserialize, Deserializer, Serialize, Serializer};
94+
use crate::{macros::serde_conv_doc, oid::ObjectId};
95+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
9696
use serde_with::{DeserializeAs, SerializeAs};
9797

98-
/// Contains functions to serialize an ObjectId as a hex string and deserialize an
99-
/// ObjectId from a hex string
100-
/// ```rust
101-
/// # #[cfg(feature = "serde_with-3")]
102-
/// {
103-
/// # use serde::{Serialize, Deserialize};
104-
/// # use bson::serde_helpers::object_id;
105-
/// # use serde_with::serde_as;
106-
/// # use bson::oid::ObjectId;
107-
/// #[serde_as]
108-
/// #[derive(Serialize, Deserialize)]
109-
/// struct Item {
110-
/// #[serde_as(as = "object_id::AsHexString")]
111-
/// pub id: ObjectId,
112-
/// }
113-
/// # }
114-
/// ```
115-
pub struct AsHexString;
116-
117-
impl SerializeAs<ObjectId> for AsHexString {
118-
fn serialize_as<S>(val: &ObjectId, serializer: S) -> Result<S::Ok, S::Error>
119-
where
120-
S: Serializer,
121-
{
122-
val.to_hex().serialize(serializer)
123-
}
124-
}
125-
126-
impl<'de> DeserializeAs<'de, ObjectId> for AsHexString {
127-
fn deserialize_as<D>(deserializer: D) -> Result<ObjectId, D::Error>
128-
where
129-
D: Deserializer<'de>,
130-
{
131-
let hex_string = String::deserialize(deserializer)?;
132-
ObjectId::parse_str(&hex_string).map_err(serde::de::Error::custom)
133-
}
134-
}
135-
136-
/// Contains functions to serialize a hex string as an ObjectId and deserialize a
137-
/// hex string from an ObjectId
138-
/// ```rust
139-
/// # #[cfg(feature = "serde_with-3")]
140-
/// {
141-
/// # use serde::{Serialize, Deserialize};
142-
/// # use bson::serde_helpers::object_id;
143-
/// # use serde_with::serde_as;
144-
/// #[serde_as]
145-
/// #[derive(Serialize, Deserialize)]
146-
/// struct Item {
147-
/// #[serde_as(as = "object_id::FromHexString")]
148-
/// pub id: String,
149-
/// }
150-
/// # }
151-
/// ```
152-
pub struct FromHexString;
153-
154-
impl SerializeAs<String> for FromHexString {
155-
fn serialize_as<S>(val: &String, serializer: S) -> Result<S::Ok, S::Error>
156-
where
157-
S: Serializer,
158-
{
159-
match ObjectId::parse_str(val) {
160-
Ok(oid) => oid.serialize(serializer),
161-
Err(e) => Err(ser::Error::custom(format!(
162-
"cannot convert {} to ObjectId: {}",
163-
val, e
164-
))),
165-
}
98+
serde_conv_doc!(
99+
/// Contains functions to serialize an ObjectId as a hex string and deserialize an
100+
/// ObjectId from a hex string
101+
/// ```rust
102+
/// # #[cfg(feature = "serde_with-3")]
103+
/// {
104+
/// # use serde::{Serialize, Deserialize};
105+
/// # use bson::serde_helpers::object_id;
106+
/// # use serde_with::serde_as;
107+
/// # use bson::oid::ObjectId;
108+
/// #[serde_as]
109+
/// #[derive(Serialize, Deserialize)]
110+
/// struct Item {
111+
/// #[serde_as(as = "object_id::AsHexString")]
112+
/// pub id: ObjectId,
113+
/// }
114+
/// # }
115+
/// ```
116+
pub AsHexString,
117+
ObjectId,
118+
|oid: &ObjectId| -> Result<String, String> {
119+
Ok(oid.to_hex())
120+
},
121+
|hex: String| -> Result<ObjectId, String> {
122+
ObjectId::parse_str(&hex).map_err(|e| format!("Invalid ObjectId string, {}: {}", hex, e))
166123
}
167-
}
124+
);
168125

169-
impl<'de> DeserializeAs<'de, String> for FromHexString {
170-
fn deserialize_as<D>(deserializer: D) -> Result<String, D::Error>
171-
where
172-
D: Deserializer<'de>,
173-
{
174-
let object_id = ObjectId::deserialize(deserializer)?;
175-
Ok(object_id.to_hex())
126+
serde_conv_doc!(
127+
/// Contains functions to serialize a hex string as an ObjectId and deserialize a
128+
/// hex string from an ObjectId
129+
/// ```rust
130+
/// # #[cfg(feature = "serde_with-3")]
131+
/// {
132+
/// # use serde::{Serialize, Deserialize};
133+
/// # use bson::serde_helpers::object_id;
134+
/// # use serde_with::serde_as;
135+
/// #[serde_as]
136+
/// #[derive(Serialize, Deserialize)]
137+
/// struct Item {
138+
/// #[serde_as(as = "object_id::FromHexString")]
139+
/// pub id: String,
140+
/// }
141+
/// # }
142+
/// ```
143+
pub FromHexString,
144+
String,
145+
|hex: &String| -> Result<ObjectId, String> {
146+
ObjectId::parse_str(hex).map_err(|e| format!("Invalid ObjectId string, {}: {}", hex, e))
147+
},
148+
|oid: ObjectId| -> Result<String, String> {
149+
Ok(oid.to_hex())
176150
}
177-
}
151+
);
178152
}
179153

180154
/// Contains functions to serialize a u32 as an f64 (BSON double) and deserialize a

0 commit comments

Comments
 (0)