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",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bstringify"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd769563b4ea2953e2825c9e6b7470a5f55f67e0be00030bf3e390a2a6071f64"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.13.0"
|
version = "3.13.0"
|
||||||
@ -331,6 +337,7 @@ dependencies = [
|
|||||||
"async-stream",
|
"async-stream",
|
||||||
"async_zip",
|
"async_zip",
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.4.0",
|
||||||
|
"bstringify",
|
||||||
"fmmap",
|
"fmmap",
|
||||||
"futures",
|
"futures",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -48,6 +48,7 @@ memmap = { version = "0.7.0", optional = true }
|
|||||||
pin-project = { version = "1.1.3", optional = true }
|
pin-project = { version = "1.1.3", optional = true }
|
||||||
postcard = { version = "1.0.6", default-features = false }
|
postcard = { version = "1.0.6", default-features = false }
|
||||||
bitflags = "2.4.0"
|
bitflags = "2.4.0"
|
||||||
|
bstringify = "0.1.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = { version = "1.31.0", features = ["yaml"] }
|
insta = { version = "1.31.0", features = ["yaml"] }
|
||||||
|
@ -8,6 +8,7 @@ use serde::{
|
|||||||
Deserialize, Deserializer,
|
Deserialize, Deserializer,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use bstringify::bstringify;
|
||||||
use serde::ser::SerializeStruct;
|
use serde::ser::SerializeStruct;
|
||||||
|
|
||||||
use bitflags::bitflags;
|
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 {
|
macro_rules! flags_from_strings {
|
||||||
(($self:ident, $flags:ident, $FlagsType:ident) <- {$($field:ident:$flag:ident),+} ) => {$(
|
(($self:ident, $flags:ident, $FlagsType:ident) <- {$($field:ident:$flag:ident),+} ) => {$(
|
||||||
if !$self.$field.is_empty() {
|
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 {
|
macro_rules! serialize_fields {
|
||||||
(($self:ident, $flags:ident, $state:ident, $FlagsType:ident) <- {$($field:ident:$flag:ident),+} ) => {$(
|
(($self:ident, $flags:ident, $state:ident, $FlagsType:ident) <- {$($field:ident:$flag:ident),+} ) => {$(
|
||||||
if $flags.intersects($FlagsType::$flag) {
|
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 {
|
impl<'de> Deserialize<'de> for BatchInfo {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
@ -264,45 +376,19 @@ impl<'de> Deserialize<'de> for BatchInfo {
|
|||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
match value {
|
match_visit_str_fields!(value => BatchField {
|
||||||
"filename" => Ok(BatchField::filename),
|
filename, description, author, comment, url, date, processed_by, redacted_by,
|
||||||
"description" => Ok(BatchField::description),
|
copyright, theme, kind, source, rating, _flags, __ignore
|
||||||
"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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
match value {
|
match_visit_str_fields!([bytes] value => BatchField {
|
||||||
b"filename" => Ok(BatchField::filename),
|
filename, description, author, comment, url, date, processed_by, redacted_by,
|
||||||
b"description" => Ok(BatchField::description),
|
copyright, theme, kind, source, rating, _flags, __ignore
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'de> Deserialize<'de> for BatchField {
|
impl<'de> Deserialize<'de> for BatchField {
|
||||||
@ -333,16 +419,6 @@ impl<'de> Deserialize<'de> for BatchInfo {
|
|||||||
)
|
)
|
||||||
.unwrap_or_default();
|
.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) <- {
|
seq_read_strings!((flags, seq, BatchFlags) <- {
|
||||||
filename: FILENAME, description: DESCRIPTION, author: AUTHOR, comment: COMMENT, url: URL, date: DATE, processed_by: PROCESSED,
|
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
|
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 source: Option<String> = None;
|
||||||
let mut rating: 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)? {
|
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,
|
filename, description, author, comment, url, date, processed_by,
|
||||||
redacted_by, copyright, theme, kind, source, rating
|
redacted_by, copyright, theme, kind, source, rating
|
||||||
});
|
});
|
||||||
@ -534,55 +590,19 @@ impl<'de> Deserialize<'de> for Question {
|
|||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
match value {
|
match_visit_str_fields!(value => QuestionField {
|
||||||
"num" => Ok(QuestionField::num),
|
num, id, description, answer, author, comment, comment1, tour, url, date, processed_by,
|
||||||
"id" => Ok(QuestionField::id),
|
redacted_by, copyright, theme, kind, source, rating, batch_info, _flags, __ignore
|
||||||
"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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
|
||||||
where
|
where
|
||||||
E: serde::de::Error,
|
E: serde::de::Error,
|
||||||
{
|
{
|
||||||
match value {
|
match_visit_str_fields!([bytes] value => QuestionField {
|
||||||
b"num" => Ok(QuestionField::num),
|
num, id, description, answer, author, comment, comment1, tour, url, date, processed_by,
|
||||||
b"id" => Ok(QuestionField::id),
|
redacted_by, copyright, theme, kind, source, rating, batch_info, _flags, __ignore
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'de> Deserialize<'de> for QuestionField {
|
impl<'de> Deserialize<'de> for QuestionField {
|
||||||
@ -614,16 +634,6 @@ impl<'de> Deserialize<'de> for Question {
|
|||||||
)
|
)
|
||||||
.unwrap_or_default();
|
.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) {
|
let num: u32 = if _flags.intersects(QuestionFlags::NUM) {
|
||||||
SeqAccess::next_element::<u32>(&mut seq)?.unwrap_or_default()
|
SeqAccess::next_element::<u32>(&mut seq)?.unwrap_or_default()
|
||||||
} else {
|
} else {
|
||||||
@ -700,26 +710,6 @@ impl<'de> Deserialize<'de> for Question {
|
|||||||
let mut rating: Option<String> = None;
|
let mut rating: Option<String> = None;
|
||||||
let mut batch_info: Option<BatchInfo> = 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)? {
|
while let Some(key) = MapAccess::next_key::<QuestionField>(&mut map)? {
|
||||||
match_map_fields!((map, key, QuestionField) <- {
|
match_map_fields!((map, key, QuestionField) <- {
|
||||||
num: u32, id: String, description: String, answer: String, author: String, comment: String,
|
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 {
|
let id = id.ok_or(<V::Error as serde::de::Error>::missing_field("id"))?;
|
||||||
Some(value) => value,
|
let description = description
|
||||||
_ => return Err(<V::Error as serde::de::Error>::missing_field("id")),
|
.ok_or(<V::Error as serde::de::Error>::missing_field("description"))?;
|
||||||
};
|
let answer =
|
||||||
let description = match description {
|
answer.ok_or(<V::Error as serde::de::Error>::missing_field("answer"))?;
|
||||||
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 num = num.unwrap_or_default();
|
let num = num.unwrap_or_default();
|
||||||
let author = author.unwrap_or_default();
|
let author = author.unwrap_or_default();
|
||||||
|
Loading…
Reference in New Issue
Block a user