From 7f900ea6d966b4735f780c72eccf440e94e88b79 Mon Sep 17 00:00:00 2001 From: QuantumExplorer Date: Fri, 4 Oct 2024 16:59:14 +0700 Subject: [PATCH] feat: serde support for a basic structs (#344) * feat: serde support for a basic structs * cleanup --- grovedb/Cargo.toml | 2 + grovedb/src/debugger.rs | 3 +- grovedb/src/element/mod.rs | 1 + grovedb/src/query/mod.rs | 2 + grovedb/src/reference_path.rs | 29 +---- merk/Cargo.toml | 2 + merk/src/proofs/query/mod.rs | 2 + merk/src/proofs/query/query_item/mod.rs | 147 ++++++++++++++++++++++++ 8 files changed, 159 insertions(+), 29 deletions(-) diff --git a/grovedb/Cargo.toml b/grovedb/Cargo.toml index bac51495..f84735c0 100644 --- a/grovedb/Cargo.toml +++ b/grovedb/Cargo.toml @@ -36,6 +36,7 @@ tokio-util = { version = "0.7.12", optional = true } tokio = { version = "1.40.0", features = ["rt-multi-thread", "net"], optional = true } tower-http = { version = "0.5.2", features = ["fs"], optional = true } zip-extensions = { version ="0.6.2", optional = true } +serde = { version = "1.0.210", features = ["derive"], optional = true } [dev-dependencies] grovedb-epoch-based-storage-flags = { version = "2.1.0", path = "../grovedb-epoch-based-storage-flags" } @@ -52,6 +53,7 @@ harness = false [features] default = ["full"] proof_debug = ["grovedb-merk/proof_debug"] +serde = ["dep:serde", "grovedb-merk/serde", "indexmap/serde"] full = [ "grovedb-merk/full", "thiserror", diff --git a/grovedb/src/debugger.rs b/grovedb/src/debugger.rs index 1c07504f..1920ff81 100644 --- a/grovedb/src/debugger.rs +++ b/grovedb/src/debugger.rs @@ -85,7 +85,8 @@ where let now = Instant::now(); let mut lock = state.sessions.write().await; let to_delete: Vec = lock.iter().filter_map( - |(id, session)| (session.last_access < now - SESSION_TIMEOUT).then_some(*id) + |(id, session)| + (session.last_access < now - SESSION_TIMEOUT).then_some(*id) ).collect(); to_delete.into_iter().for_each(|id| { lock.remove(&id); }); diff --git a/grovedb/src/element/mod.rs b/grovedb/src/element/mod.rs index b1667ffb..9986c624 100644 --- a/grovedb/src/element/mod.rs +++ b/grovedb/src/element/mod.rs @@ -71,6 +71,7 @@ pub type SumValue = i64; /// of how serialization works. #[derive(Clone, Encode, Decode, PartialEq, Eq, Hash)] #[cfg_attr(not(any(feature = "full", feature = "visualize")), derive(Debug))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Element { /// An ordinary value Item(Vec, Option), diff --git a/grovedb/src/query/mod.rs b/grovedb/src/query/mod.rs index a1443836..07930c4e 100644 --- a/grovedb/src/query/mod.rs +++ b/grovedb/src/query/mod.rs @@ -23,6 +23,7 @@ use crate::Error; #[cfg(any(feature = "full", feature = "verify"))] #[derive(Debug, Clone, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] /// Path query /// /// Represents a path to a specific GroveDB tree and a corresponding query to @@ -50,6 +51,7 @@ impl fmt::Display for PathQuery { #[cfg(any(feature = "full", feature = "verify"))] #[derive(Debug, Clone, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] /// Holds a query to apply to a tree and an optional limit/offset value. /// Limit and offset values affect the size of the result set. pub struct SizedQuery { diff --git a/grovedb/src/reference_path.rs b/grovedb/src/reference_path.rs index aa01d400..fcfeee6e 100644 --- a/grovedb/src/reference_path.rs +++ b/grovedb/src/reference_path.rs @@ -1,31 +1,3 @@ -// MIT LICENSE -// -// Copyright (c) 2021 Dash Core Group -// -// Permission is hereby granted, free of charge, to any -// person obtaining a copy of this software and associated -// documentation files (the "Software"), to deal in the -// Software without restriction, including without -// limitation the rights to use, copy, modify, merge, -// publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software -// is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice -// shall be included in all copies or substantial portions -// of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - //! Space efficient methods for referencing other elements in GroveDB #[cfg(any(feature = "full", feature = "verify"))] @@ -42,6 +14,7 @@ use crate::Error; #[cfg(any(feature = "full", feature = "verify"))] #[cfg_attr(not(any(feature = "full", feature = "visualize")), derive(Debug))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] /// Reference path variants #[derive(Hash, Eq, PartialEq, Encode, Decode, Clone)] pub enum ReferencePathType { diff --git a/merk/Cargo.toml b/merk/Cargo.toml index 0806878e..cdb47bdf 100644 --- a/merk/Cargo.toml +++ b/merk/Cargo.toml @@ -23,6 +23,7 @@ hex = "0.4.3" indexmap = "2.2.6" integer-encoding = "4.0.0" thiserror = "1.0.58" +serde = { version = "1.0.210", features = ["derive"], optional = true } [dependencies.time] version = "0.3.34" @@ -56,6 +57,7 @@ optional = true [features] default = ["full"] proof_debug = [] +serde = ["dep:serde", "indexmap/serde"] full = ["rand", "time", "colored", diff --git a/merk/src/proofs/query/mod.rs b/merk/src/proofs/query/mod.rs index 6f1e506a..b1543153 100644 --- a/merk/src/proofs/query/mod.rs +++ b/merk/src/proofs/query/mod.rs @@ -69,6 +69,7 @@ pub type PathKey = (Path, Key); #[cfg(any(feature = "full", feature = "verify"))] #[derive(Debug, Default, Clone, PartialEq, Encode, Decode)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] /// Subquery branch pub struct SubqueryBranch { /// Subquery path @@ -111,6 +112,7 @@ impl SubqueryBranch { /// `Query` represents one or more keys or ranges of keys, which can be used to /// resolve a proof which will include all the requested values. #[derive(Debug, Default, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Query { /// Items pub items: Vec, diff --git a/merk/src/proofs/query/query_item/mod.rs b/merk/src/proofs/query/query_item/mod.rs index 2209f583..d4f7fd8b 100644 --- a/merk/src/proofs/query/query_item/mod.rs +++ b/merk/src/proofs/query/query_item/mod.rs @@ -15,6 +15,10 @@ use bincode::{enc::write::Writer, error::DecodeError, BorrowDecode, Decode, Enco use grovedb_costs::{CostContext, CostsExt, OperationCost}; #[cfg(feature = "full")] use grovedb_storage::RawIterator; +#[cfg(feature = "serde")] +use serde::de::VariantAccess; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(any(feature = "full", feature = "verify"))] use crate::error::Error; @@ -36,6 +40,149 @@ pub enum QueryItem { RangeAfterToInclusive(RangeInclusive>), } +#[cfg(feature = "serde")] +impl Serialize for QueryItem { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + QueryItem::Key(key) => serializer.serialize_newtype_variant("QueryItem", 0, "Key", key), + QueryItem::Range(range) => { + serializer.serialize_newtype_variant("QueryItem", 1, "Range", &range) + } + QueryItem::RangeInclusive(range) => { + serializer.serialize_newtype_variant("QueryItem", 2, "RangeInclusive", range) + } + QueryItem::RangeFull(_) => { + serializer.serialize_unit_variant("QueryItem", 3, "RangeFull") + } + QueryItem::RangeFrom(range_from) => { + serializer.serialize_newtype_variant("QueryItem", 4, "RangeFrom", range_from) + } + QueryItem::RangeTo(range_to) => { + serializer.serialize_newtype_variant("QueryItem", 5, "RangeTo", range_to) + } + QueryItem::RangeToInclusive(range_to_inclusive) => serializer + .serialize_newtype_variant( + "QueryItem", + 6, + "RangeToInclusive", + &range_to_inclusive.end, + ), + QueryItem::RangeAfter(range_after) => { + serializer.serialize_newtype_variant("QueryItem", 7, "RangeAfter", range_after) + } + QueryItem::RangeAfterTo(range_after_to) => { + serializer.serialize_newtype_variant("QueryItem", 8, "RangeAfterTo", range_after_to) + } + QueryItem::RangeAfterToInclusive(range_after_to_inclusive) => serializer + .serialize_newtype_variant( + "QueryItem", + 9, + "RangeAfterToInclusive", + range_after_to_inclusive, + ), + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for QueryItem { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + Key, + Range, + RangeInclusive, + RangeFull, + RangeFrom, + RangeTo, + RangeToInclusive, + RangeAfter, + RangeAfterTo, + RangeAfterToInclusive, + } + + struct QueryItemVisitor; + + impl<'de> serde::de::Visitor<'de> for QueryItemVisitor { + type Value = QueryItem; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("enum QueryItem") + } + + fn visit_enum(self, data: A) -> Result + where + A: serde::de::EnumAccess<'de>, + { + let (variant, variant_access) = data.variant()?; + + match variant { + Field::Key => { + let key = variant_access.newtype_variant()?; + Ok(QueryItem::Key(key)) + } + Field::Range => { + let range = variant_access.newtype_variant()?; + Ok(QueryItem::Range(range)) + } + Field::RangeInclusive => { + let range_inclusive = variant_access.newtype_variant()?; + Ok(QueryItem::RangeInclusive(range_inclusive)) + } + Field::RangeFull => Ok(QueryItem::RangeFull(RangeFull)), + Field::RangeFrom => { + let range_from = variant_access.newtype_variant()?; + Ok(QueryItem::RangeFrom(range_from)) + } + Field::RangeTo => { + let range_to = variant_access.newtype_variant()?; + Ok(QueryItem::RangeTo(range_to)) + } + Field::RangeToInclusive => { + // Deserialize the `Vec` for the `end` of the range + let end = variant_access.newtype_variant()?; + Ok(QueryItem::RangeToInclusive(..=end)) + } + Field::RangeAfter => { + let range_after = variant_access.newtype_variant()?; + Ok(QueryItem::RangeAfter(range_after)) + } + Field::RangeAfterTo => { + let range_after_to = variant_access.newtype_variant()?; + Ok(QueryItem::RangeAfterTo(range_after_to)) + } + Field::RangeAfterToInclusive => { + let range_after_to_inclusive = variant_access.newtype_variant()?; + Ok(QueryItem::RangeAfterToInclusive(range_after_to_inclusive)) + } + } + } + } + + const VARIANTS: &[&str] = &[ + "Key", + "Range", + "RangeInclusive", + "RangeFull", + "RangeFrom", + "RangeTo", + "RangeToInclusive", + "RangeAfter", + "RangeAfterTo", + "RangeAfterToInclusive", + ]; + + deserializer.deserialize_enum("QueryItem", VARIANTS, QueryItemVisitor) + } +} + #[cfg(any(feature = "full", feature = "verify"))] impl Encode for QueryItem { fn encode(