questions: more macro_rules
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
6a21543890
commit
9934825a93
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -228,6 +228,12 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstringify"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd769563b4ea2953e2825c9e6b7470a5f55f67e0be00030bf3e390a2a6071f64"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.13.0"
|
||||
@ -331,6 +337,7 @@ dependencies = [
|
||||
"async-stream",
|
||||
"async_zip",
|
||||
"bitflags 2.4.0",
|
||||
"bstringify",
|
||||
"fmmap",
|
||||
"futures",
|
||||
"futures-core",
|
||||
|
@ -48,6 +48,7 @@ memmap = { version = "0.7.0", optional = true }
|
||||
pin-project = { version = "1.1.3", optional = true }
|
||||
postcard = { version = "1.0.6", default-features = false }
|
||||
bitflags = "2.4.0"
|
||||
bstringify = "0.1.2"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = { version = "1.31.0", features = ["yaml"] }
|
||||
|
@ -8,6 +8,7 @@ use serde::{
|
||||
Deserialize, Deserializer,
|
||||
};
|
||||
|
||||
use bstringify::bstringify;
|
||||
use serde::ser::SerializeStruct;
|
||||
|
||||
use bitflags::bitflags;
|
||||
@ -102,6 +103,12 @@ impl BatchInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// fill flags, if string field is not empty set bit flag
|
||||
///
|
||||
/// Example:
|
||||
/// ```ignore
|
||||
/// flags_from_strings!((self, flags, BatchFlags) <- { filename: FILENAME, description: DESCRIPTION, ... });
|
||||
/// ```
|
||||
macro_rules! flags_from_strings {
|
||||
(($self:ident, $flags:ident, $FlagsType:ident) <- {$($field:ident:$flag:ident),+} ) => {$(
|
||||
if !$self.$field.is_empty() {
|
||||
@ -110,6 +117,12 @@ macro_rules! flags_from_strings {
|
||||
)+}
|
||||
}
|
||||
|
||||
/// serialize fields, if bit flag is not set then skip
|
||||
///
|
||||
/// Example:
|
||||
/// ```ignore
|
||||
/// serialize_fields!((self, flags, state, BatchFlags) <- { filename: FILENAME, description: DESCRIPTION, ... });
|
||||
/// ```
|
||||
macro_rules! serialize_fields {
|
||||
(($self:ident, $flags:ident, $state:ident, $FlagsType:ident) <- {$($field:ident:$flag:ident),+} ) => {$(
|
||||
if $flags.intersects($FlagsType::$flag) {
|
||||
@ -208,6 +221,105 @@ impl serde::Serialize for Question {
|
||||
}
|
||||
}
|
||||
|
||||
/// match field name on _FieldVisitor::visit_str or ::visit_bytes
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// str:
|
||||
/// ```ignore
|
||||
/// fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
/// where E: serde::de::Error, {
|
||||
/// match_visit_str_fields!(value => BatchField {filename, description, ...})
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// or bytes:
|
||||
/// ```ignore
|
||||
/// fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||
/// where E: serde::de::Error, {
|
||||
/// match_visit_str_fields!([bytes] value => BatchField {filename, description, ...})
|
||||
/// }
|
||||
/// ```
|
||||
macro_rules! match_visit_str_fields {
|
||||
($value:ident => $FieldType:ty {$($field:ident),+}) => {
|
||||
match $value {
|
||||
$(
|
||||
std::stringify!($field) => Ok(<$FieldType>::$field),
|
||||
)+
|
||||
_ => Ok(<$FieldType>::__ignore),
|
||||
}
|
||||
};
|
||||
([bytes] $value:ident => $FieldType:ty {$($field:ident),+}) => {
|
||||
match $value {
|
||||
$(
|
||||
bstringify!($field) => Ok(<$FieldType>::$field),
|
||||
)+
|
||||
_ => Ok(<$FieldType>::__ignore),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// for seq access, deserialize string fields if bit flag is set, else read as default (empty)
|
||||
macro_rules! seq_read_strings {
|
||||
(($flags:ident, $seq:ident, $FlagsType:ty) <- {$($field:ident:$flag:ident),+} ) => {$(
|
||||
let $field: String = if $flags.intersects(<$FlagsType>::$flag) {
|
||||
SeqAccess::next_element::<String>(&mut $seq)?.unwrap_or_default()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
)+}
|
||||
}
|
||||
|
||||
/// for map access, match and deserialize fields + check for duplicated fields
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// variant for mixed value types:
|
||||
/// ```ignore
|
||||
/// match_map_fields!((map, key, QuestionField) <- {num: u32, id: String, ...});
|
||||
/// ```
|
||||
///
|
||||
/// variant for single value type:
|
||||
/// ```ignore
|
||||
/// match_map_fields!((map, key, BatchField, String) <- {filename, url, ...});
|
||||
/// ```
|
||||
macro_rules! match_map_fields {
|
||||
(($map:ident, $key:ident, $FieldType:ty) <- {$($field:ident:$FieldValueType:ty),+} ) => {
|
||||
match $key {
|
||||
$(
|
||||
<$FieldType>::$field => {
|
||||
if $field.is_some() {
|
||||
return Err(<V::Error as serde::de::Error>::duplicate_field(
|
||||
std::stringify!($field),
|
||||
));
|
||||
}
|
||||
$field = Some(MapAccess::next_value::<$FieldValueType>(&mut $map)?);
|
||||
},
|
||||
)+
|
||||
_ => {
|
||||
let _ = MapAccess::next_value::<serde::de::IgnoredAny>(&mut $map)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
(($map:ident, $key:ident, $FieldType:ty, $FieldValueType:ty) <- {$($field:ident),+} ) => {
|
||||
match $key {
|
||||
$(
|
||||
<$FieldType>::$field => {
|
||||
if $field.is_some() {
|
||||
return Err(<V::Error as serde::de::Error>::duplicate_field(
|
||||
std::stringify!($field),
|
||||
));
|
||||
}
|
||||
$field = Some(MapAccess::next_value::<$FieldValueType>(&mut $map)?);
|
||||
},
|
||||
)+
|
||||
_ => {
|
||||
let _ = MapAccess::next_value::<serde::de::IgnoredAny>(&mut $map)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for BatchInfo {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
@ -264,45 +376,19 @@ impl<'de> Deserialize<'de> for BatchInfo {
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match value {
|
||||
"filename" => Ok(BatchField::filename),
|
||||
"description" => Ok(BatchField::description),
|
||||
"author" => Ok(BatchField::author),
|
||||
"comment" => Ok(BatchField::comment),
|
||||
"url" => Ok(BatchField::url),
|
||||
"date" => Ok(BatchField::date),
|
||||
"processed_by" => Ok(BatchField::processed_by),
|
||||
"redacted_by" => Ok(BatchField::redacted_by),
|
||||
"copyright" => Ok(BatchField::copyright),
|
||||
"theme" => Ok(BatchField::theme),
|
||||
"kind" => Ok(BatchField::kind),
|
||||
"source" => Ok(BatchField::source),
|
||||
"rating" => Ok(BatchField::rating),
|
||||
"_flags" => Ok(BatchField::_flags),
|
||||
_ => Ok(BatchField::__ignore),
|
||||
}
|
||||
match_visit_str_fields!(value => BatchField {
|
||||
filename, description, author, comment, url, date, processed_by, redacted_by,
|
||||
copyright, theme, kind, source, rating, _flags, __ignore
|
||||
})
|
||||
}
|
||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match value {
|
||||
b"filename" => Ok(BatchField::filename),
|
||||
b"description" => Ok(BatchField::description),
|
||||
b"author" => Ok(BatchField::author),
|
||||
b"comment" => Ok(BatchField::comment),
|
||||
b"url" => Ok(BatchField::url),
|
||||
b"date" => Ok(BatchField::date),
|
||||
b"processed_by" => Ok(BatchField::processed_by),
|
||||
b"redacted_by" => Ok(BatchField::redacted_by),
|
||||
b"copyright" => Ok(BatchField::copyright),
|
||||
b"theme" => Ok(BatchField::theme),
|
||||
b"kind" => Ok(BatchField::kind),
|
||||
b"source" => Ok(BatchField::source),
|
||||
b"rating" => Ok(BatchField::rating),
|
||||
b"_flags" => Ok(BatchField::_flags),
|
||||
_ => Ok(BatchField::__ignore),
|
||||
}
|
||||
match_visit_str_fields!([bytes] value => BatchField {
|
||||
filename, description, author, comment, url, date, processed_by, redacted_by,
|
||||
copyright, theme, kind, source, rating, _flags, __ignore
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<'de> Deserialize<'de> for BatchField {
|
||||
@ -333,16 +419,6 @@ impl<'de> Deserialize<'de> for BatchInfo {
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
macro_rules! seq_read_strings {
|
||||
(($flags:ident, $seq:ident, $FlagsType:ident) <- {$($field:ident:$flag:ident),+} ) => {$(
|
||||
let $field: String = if $flags.intersects($FlagsType::$flag) {
|
||||
SeqAccess::next_element::<String>(&mut $seq)?.unwrap_or_default()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
)+}
|
||||
}
|
||||
|
||||
seq_read_strings!((flags, seq, BatchFlags) <- {
|
||||
filename: FILENAME, description: DESCRIPTION, author: AUTHOR, comment: COMMENT, url: URL, date: DATE, processed_by: PROCESSED,
|
||||
redacted_by: REDACTED, copyright: COPYRIGHT, theme: THEME, kind: KIND, source: SOURCE, rating: RATING
|
||||
@ -383,28 +459,8 @@ impl<'de> Deserialize<'de> for BatchInfo {
|
||||
let mut source: Option<String> = None;
|
||||
let mut rating: Option<String> = None;
|
||||
|
||||
macro_rules! match_map_fields {
|
||||
(($map:ident, $key:ident, $FieldType:ident) <- {$($field:ident),+} ) => {
|
||||
match $key {
|
||||
$(
|
||||
$FieldType::$field => {
|
||||
if $field.is_some() {
|
||||
return Err(<V::Error as serde::de::Error>::duplicate_field(
|
||||
std::stringify!($field),
|
||||
));
|
||||
}
|
||||
$field = Some(MapAccess::next_value::<String>(&mut $map)?);
|
||||
},
|
||||
)+
|
||||
_ => {
|
||||
let _ = MapAccess::next_value::<serde::de::IgnoredAny>(&mut $map)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(key) = MapAccess::next_key::<BatchField>(&mut map)? {
|
||||
match_map_fields!((map, key, BatchField) <- {
|
||||
match_map_fields!((map, key, BatchField, String) <- {
|
||||
filename, description, author, comment, url, date, processed_by,
|
||||
redacted_by, copyright, theme, kind, source, rating
|
||||
});
|
||||
@ -534,55 +590,19 @@ impl<'de> Deserialize<'de> for Question {
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match value {
|
||||
"num" => Ok(QuestionField::num),
|
||||
"id" => Ok(QuestionField::id),
|
||||
"description" => Ok(QuestionField::description),
|
||||
"answer" => Ok(QuestionField::answer),
|
||||
"author" => Ok(QuestionField::author),
|
||||
"comment" => Ok(QuestionField::comment),
|
||||
"comment1" => Ok(QuestionField::comment1),
|
||||
"tour" => Ok(QuestionField::tour),
|
||||
"url" => Ok(QuestionField::url),
|
||||
"date" => Ok(QuestionField::date),
|
||||
"processed_by" => Ok(QuestionField::processed_by),
|
||||
"redacted_by" => Ok(QuestionField::redacted_by),
|
||||
"copyright" => Ok(QuestionField::copyright),
|
||||
"theme" => Ok(QuestionField::theme),
|
||||
"kind" => Ok(QuestionField::kind),
|
||||
"source" => Ok(QuestionField::source),
|
||||
"rating" => Ok(QuestionField::rating),
|
||||
"batch_info" => Ok(QuestionField::batch_info),
|
||||
"_flags" => Ok(QuestionField::_flags),
|
||||
_ => Ok(QuestionField::__ignore),
|
||||
}
|
||||
match_visit_str_fields!(value => QuestionField {
|
||||
num, id, description, answer, author, comment, comment1, tour, url, date, processed_by,
|
||||
redacted_by, copyright, theme, kind, source, rating, batch_info, _flags, __ignore
|
||||
})
|
||||
}
|
||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
match value {
|
||||
b"num" => Ok(QuestionField::num),
|
||||
b"id" => Ok(QuestionField::id),
|
||||
b"description" => Ok(QuestionField::description),
|
||||
b"answer" => Ok(QuestionField::answer),
|
||||
b"author" => Ok(QuestionField::author),
|
||||
b"comment" => Ok(QuestionField::comment),
|
||||
b"comment1" => Ok(QuestionField::comment1),
|
||||
b"tour" => Ok(QuestionField::tour),
|
||||
b"url" => Ok(QuestionField::url),
|
||||
b"date" => Ok(QuestionField::date),
|
||||
b"processed_by" => Ok(QuestionField::processed_by),
|
||||
b"redacted_by" => Ok(QuestionField::redacted_by),
|
||||
b"copyright" => Ok(QuestionField::copyright),
|
||||
b"theme" => Ok(QuestionField::theme),
|
||||
b"kind" => Ok(QuestionField::kind),
|
||||
b"source" => Ok(QuestionField::source),
|
||||
b"rating" => Ok(QuestionField::rating),
|
||||
b"batch_info" => Ok(QuestionField::batch_info),
|
||||
b"_flags" => Ok(QuestionField::_flags),
|
||||
_ => Ok(QuestionField::__ignore),
|
||||
}
|
||||
match_visit_str_fields!([bytes] value => QuestionField {
|
||||
num, id, description, answer, author, comment, comment1, tour, url, date, processed_by,
|
||||
redacted_by, copyright, theme, kind, source, rating, batch_info, _flags, __ignore
|
||||
})
|
||||
}
|
||||
}
|
||||
impl<'de> Deserialize<'de> for QuestionField {
|
||||
@ -614,16 +634,6 @@ impl<'de> Deserialize<'de> for Question {
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
macro_rules! seq_read_strings {
|
||||
(($flags:ident, $seq:ident, $FlagsType:ident) <- {$($field:ident:$flag:ident),+} ) => {$(
|
||||
let $field: String = if $flags.intersects($FlagsType::$flag) {
|
||||
SeqAccess::next_element::<String>(&mut $seq)?.unwrap_or_default()
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
)+}
|
||||
}
|
||||
|
||||
let num: u32 = if _flags.intersects(QuestionFlags::NUM) {
|
||||
SeqAccess::next_element::<u32>(&mut seq)?.unwrap_or_default()
|
||||
} else {
|
||||
@ -700,26 +710,6 @@ impl<'de> Deserialize<'de> for Question {
|
||||
let mut rating: Option<String> = None;
|
||||
let mut batch_info: Option<BatchInfo> = None;
|
||||
|
||||
macro_rules! match_map_fields {
|
||||
(($map:ident, $key:ident, $FieldType:ident) <- {$($field:ident:$FieldValueType:ident),+} ) => {
|
||||
match $key {
|
||||
$(
|
||||
$FieldType::$field => {
|
||||
if $field.is_some() {
|
||||
return Err(<V::Error as serde::de::Error>::duplicate_field(
|
||||
std::stringify!($field),
|
||||
));
|
||||
}
|
||||
$field = Some(MapAccess::next_value::<$FieldValueType>(&mut $map)?);
|
||||
},
|
||||
)+
|
||||
_ => {
|
||||
let _ = MapAccess::next_value::<serde::de::IgnoredAny>(&mut $map)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(key) = MapAccess::next_key::<QuestionField>(&mut map)? {
|
||||
match_map_fields!((map, key, QuestionField) <- {
|
||||
num: u32, id: String, description: String, answer: String, author: String, comment: String,
|
||||
@ -728,18 +718,11 @@ impl<'de> Deserialize<'de> for Question {
|
||||
});
|
||||
}
|
||||
|
||||
let id = match id {
|
||||
Some(value) => value,
|
||||
_ => return Err(<V::Error as serde::de::Error>::missing_field("id")),
|
||||
};
|
||||
let description = match description {
|
||||
Some(value) => value,
|
||||
_ => return Err(<V::Error as serde::de::Error>::missing_field("description")),
|
||||
};
|
||||
let answer = match answer {
|
||||
Some(value) => value,
|
||||
_ => return Err(<V::Error as serde::de::Error>::missing_field("answer")),
|
||||
};
|
||||
let id = id.ok_or(<V::Error as serde::de::Error>::missing_field("id"))?;
|
||||
let description = description
|
||||
.ok_or(<V::Error as serde::de::Error>::missing_field("description"))?;
|
||||
let answer =
|
||||
answer.ok_or(<V::Error as serde::de::Error>::missing_field("answer"))?;
|
||||
|
||||
let num = num.unwrap_or_default();
|
||||
let author = author.unwrap_or_default();
|
||||
|
Loading…
Reference in New Issue
Block a user