From bd1fc756bd2b1024bd0eafd42de85c8397c1653a Mon Sep 17 00:00:00 2001 From: PoiScript Date: Thu, 27 Jun 2019 16:59:59 +0800 Subject: [PATCH] refactor(serde): use serde derive macro --- Cargo.toml | 4 +- src/elements/block.rs | 2 + src/elements/clock.rs | 5 + src/elements/cookie.rs | 1 + src/elements/drawer.rs | 1 + src/elements/dyn_block.rs | 2 + src/elements/fn_def.rs | 1 + src/elements/fn_ref.rs | 3 + src/elements/headline.rs | 4 + src/elements/inline_call.rs | 3 + src/elements/inline_src.rs | 2 + src/elements/keyword.rs | 4 + src/elements/link.rs | 2 + src/elements/list.rs | 2 + src/elements/macros.rs | 2 + src/elements/mod.rs | 135 ++++++++++- src/elements/planning.rs | 4 + src/elements/radio_target.rs | 1 + src/elements/snippet.rs | 1 + src/elements/target.rs | 1 + src/elements/timestamp.rs | 18 +- src/serde.rs | 427 ++--------------------------------- 22 files changed, 203 insertions(+), 422 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1d49fb1..dd5000c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ travis-ci = { repository = "PoiScript/orgize" } [features] default = ["serde", "chrono"] -extra-serde-info = [] +extra-serde-info = ["serde"] [dependencies] bytecount = "0.5.1" @@ -22,7 +22,7 @@ chrono = { version = "0.4.7", optional = true } indextree = "3.2.0" jetscii = "0.4.4" memchr = "2.2.0" -serde = { version = "1.0.93", optional = true } +serde = { version = "1.0.93", optional = true, features = ["derive"] } [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/src/elements/block.rs b/src/elements/block.rs index e739cc6..ab94463 100644 --- a/src/elements/block.rs +++ b/src/elements/block.rs @@ -1,9 +1,11 @@ use memchr::{memchr, memchr_iter}; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Block<'a> { pub name: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub args: Option<&'a str>, } diff --git a/src/elements/clock.rs b/src/elements/clock.rs index 91c7091..cbf622b 100644 --- a/src/elements/clock.rs +++ b/src/elements/clock.rs @@ -5,20 +5,25 @@ use memchr::memchr; /// /// there are two types of clock: *closed* clock and *running* clock. #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub enum Clock<'a> { /// closed Clock Closed { start: Datetime<'a>, end: Datetime<'a>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] repeater: Option<&'a str>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] delay: Option<&'a str>, duration: &'a str, }, /// running Clock Running { start: Datetime<'a>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] repeater: Option<&'a str>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] delay: Option<&'a str>, }, } diff --git a/src/elements/cookie.rs b/src/elements/cookie.rs index 94ffb15..6131aa0 100644 --- a/src/elements/cookie.rs +++ b/src/elements/cookie.rs @@ -1,6 +1,7 @@ use memchr::{memchr, memchr2}; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub enum Cookie<'a> { Percent(&'a str), diff --git a/src/elements/drawer.rs b/src/elements/drawer.rs index afc0d60..c80ec25 100644 --- a/src/elements/drawer.rs +++ b/src/elements/drawer.rs @@ -1,6 +1,7 @@ use memchr::memchr_iter; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Drawer<'a> { pub name: &'a str, diff --git a/src/elements/dyn_block.rs b/src/elements/dyn_block.rs index bf4a915..b8e438a 100644 --- a/src/elements/dyn_block.rs +++ b/src/elements/dyn_block.rs @@ -1,9 +1,11 @@ use memchr::{memchr, memchr_iter}; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct DynBlock<'a> { pub block_name: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub arguments: Option<&'a str>, } diff --git a/src/elements/fn_def.rs b/src/elements/fn_def.rs index 11b558c..48851a8 100644 --- a/src/elements/fn_def.rs +++ b/src/elements/fn_def.rs @@ -1,6 +1,7 @@ use memchr::memchr; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct FnDef<'a> { pub label: &'a str, diff --git a/src/elements/fn_ref.rs b/src/elements/fn_ref.rs index 0df41e1..b9c724a 100644 --- a/src/elements/fn_ref.rs +++ b/src/elements/fn_ref.rs @@ -1,9 +1,12 @@ use memchr::{memchr2, memchr2_iter}; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct FnRef<'a> { + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub label: Option<&'a str>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub definition: Option<&'a str>, } diff --git a/src/elements/headline.rs b/src/elements/headline.rs index 5b9c54c..8717baa 100644 --- a/src/elements/headline.rs +++ b/src/elements/headline.rs @@ -7,17 +7,21 @@ pub const DEFAULT_TODO_KEYWORDS: &[&str] = &["TODO", "DONE", "NEXT", "WAITING", "LATER", "CANCELLED"]; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Headline<'a> { /// headline level, number of stars pub level: usize, /// priority cookie + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub priority: Option, /// headline tags, including the sparated colons + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Vec::is_empty"))] pub tags: Vec<&'a str>, /// headline title pub title: &'a str, /// headline keyword + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub keyword: Option<&'a str>, } diff --git a/src/elements/inline_call.rs b/src/elements/inline_call.rs index 2d1d85d..798c684 100644 --- a/src/elements/inline_call.rs +++ b/src/elements/inline_call.rs @@ -1,11 +1,14 @@ use memchr::{memchr, memchr2}; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct InlineCall<'a> { pub name: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub inside_header: Option<&'a str>, pub args: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub end_header: Option<&'a str>, } diff --git a/src/elements/inline_src.rs b/src/elements/inline_src.rs index 99cde82..2f63aae 100644 --- a/src/elements/inline_src.rs +++ b/src/elements/inline_src.rs @@ -1,9 +1,11 @@ use memchr::{memchr, memchr2}; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct InlineSrc<'a> { pub lang: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub option: Option<&'a str>, pub body: &'a str, } diff --git a/src/elements/keyword.rs b/src/elements/keyword.rs index bb692a5..89033cf 100644 --- a/src/elements/keyword.rs +++ b/src/elements/keyword.rs @@ -1,13 +1,17 @@ use memchr::{memchr, memchr2}; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Keyword<'a> { pub key: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub option: Option<&'a str>, pub value: &'a str, } +#[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct BabelCall<'a> { pub key: &'a str, diff --git a/src/elements/link.rs b/src/elements/link.rs index 8f89d81..20d56fe 100644 --- a/src/elements/link.rs +++ b/src/elements/link.rs @@ -2,9 +2,11 @@ use jetscii::Substring; use memchr::memchr; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Link<'a> { pub path: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub desc: Option<&'a str>, } diff --git a/src/elements/list.rs b/src/elements/list.rs index f4ef18a..ab49ddb 100644 --- a/src/elements/list.rs +++ b/src/elements/list.rs @@ -2,6 +2,7 @@ use memchr::memchr_iter; use std::iter::once; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct List { pub indent: usize, @@ -61,6 +62,7 @@ impl List { } #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct ListItem<'a> { pub bullet: &'a str, diff --git a/src/elements/macros.rs b/src/elements/macros.rs index 94f5b3b..da6b524 100644 --- a/src/elements/macros.rs +++ b/src/elements/macros.rs @@ -2,9 +2,11 @@ use jetscii::Substring; use memchr::memchr2; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Macros<'a> { pub name: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub arguments: Option<&'a str>, } diff --git a/src/elements/mod.rs b/src/elements/mod.rs index 5b474d4..b9dca68 100644 --- a/src/elements/mod.rs +++ b/src/elements/mod.rs @@ -20,7 +20,7 @@ mod snippet; mod target; mod timestamp; -pub mod emphasis; +pub(crate) mod emphasis; pub use self::{ block::Block, @@ -48,187 +48,304 @@ pub use self::{ use indextree::NodeId; #[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[cfg_attr(feature = "serde", serde(tag = "type"))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] pub enum Element<'a> { Root, Block { + #[cfg_attr(feature = "serde", serde(flatten))] block: Block<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, BabelCall { + #[cfg_attr(feature = "serde", serde(flatten))] call: BabelCall<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Section { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, Clock { + #[cfg_attr(feature = "serde", serde(flatten))] clock: Clock<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Cookie { + #[cfg_attr(feature = "serde", serde(flatten))] cookie: Cookie<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, RadioTarget { + #[cfg_attr(feature = "serde", serde(flatten))] radio_target: RadioTarget<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Drawer { + #[cfg_attr(feature = "serde", serde(flatten))] drawer: Drawer<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, Document { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, DynBlock { + #[cfg_attr(feature = "serde", serde(flatten))] dyn_block: DynBlock<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, FnDef { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, fn_def: FnDef<'a>, }, FnRef { + #[cfg_attr(feature = "serde", serde(flatten))] fn_ref: FnRef<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Headline { - begin: usize, - end: usize, - contents_begin: usize, - contents_end: usize, + #[cfg_attr(feature = "serde", serde(flatten))] headline: Headline<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] + begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] + end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] + contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] + contents_end: usize, }, InlineCall { + #[cfg_attr(feature = "serde", serde(flatten))] inline_call: InlineCall<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, InlineSrc { + #[cfg_attr(feature = "serde", serde(flatten))] inline_src: InlineSrc<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Keyword { + #[cfg_attr(feature = "serde", serde(flatten))] keyword: Keyword<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Link { + #[cfg_attr(feature = "serde", serde(flatten))] link: Link<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, List { + #[cfg_attr(feature = "serde", serde(flatten))] list: List, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, ListItem { + #[cfg_attr(feature = "serde", serde(flatten))] list_item: ListItem<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, Macros { + #[cfg_attr(feature = "serde", serde(flatten))] macros: Macros<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Planning { + #[cfg_attr(feature = "serde", serde(skip))] deadline: Option, + #[cfg_attr(feature = "serde", serde(skip))] scheduled: Option, + #[cfg_attr(feature = "serde", serde(skip))] closed: Option, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Snippet { - begin: usize, - end: usize, + #[cfg_attr(feature = "serde", serde(flatten))] snippet: Snippet<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] + begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] + end: usize, }, Text { value: &'a str, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Paragraph { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, Rule { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Timestamp { - begin: usize, - end: usize, + #[cfg_attr(feature = "serde", serde(flatten))] timestamp: Timestamp<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] + begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] + end: usize, }, Target { + #[cfg_attr(feature = "serde", serde(flatten))] target: Target<'a>, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, }, Bold { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, Strike { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, Italic { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, Underline { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] contents_end: usize, }, Verbatim { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, value: &'a str, }, Code { + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] begin: usize, + #[cfg_attr(all(feature = "serde", not(feature = "extra-serde-info")), serde(skip))] end: usize, value: &'a str, }, diff --git a/src/elements/planning.rs b/src/elements/planning.rs index 88357c9..d789c2d 100644 --- a/src/elements/planning.rs +++ b/src/elements/planning.rs @@ -3,13 +3,17 @@ use memchr::memchr; /// palnning elements #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Planning<'a> { /// the date when the task should be done + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub deadline: Option<&'a Timestamp<'a>>, /// the date when you should start working on the task + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub scheduled: Option<&'a Timestamp<'a>>, /// the date when the task is closed + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub closed: Option<&'a Timestamp<'a>>, } diff --git a/src/elements/radio_target.rs b/src/elements/radio_target.rs index a40df2a..c40a980 100644 --- a/src/elements/radio_target.rs +++ b/src/elements/radio_target.rs @@ -2,6 +2,7 @@ use jetscii::Substring; // TODO: text-markup, entities, latex-fragments, subscript and superscript #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct RadioTarget<'a> { contents: &'a str, diff --git a/src/elements/snippet.rs b/src/elements/snippet.rs index ec62b9a..5bb7ed8 100644 --- a/src/elements/snippet.rs +++ b/src/elements/snippet.rs @@ -2,6 +2,7 @@ use jetscii::Substring; use memchr::memchr; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Snippet<'a> { pub name: &'a str, diff --git a/src/elements/target.rs b/src/elements/target.rs index 3ba809d..5f11516 100644 --- a/src/elements/target.rs +++ b/src/elements/target.rs @@ -1,6 +1,7 @@ use jetscii::Substring; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub struct Target<'a> { pub target: &'a str, diff --git a/src/elements/timestamp.rs b/src/elements/timestamp.rs index 6ef3468..7a145b3 100644 --- a/src/elements/timestamp.rs +++ b/src/elements/timestamp.rs @@ -2,9 +2,11 @@ use memchr::memchr; use std::str::FromStr; #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, Clone, Copy)] pub struct Datetime<'a> { pub(crate) date: &'a str, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub(crate) time: Option<&'a str>, pub(crate) dayname: &'a str, } @@ -76,7 +78,8 @@ mod chrono { } #[cfg_attr(test, derive(PartialEq))] -#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[derive(Debug, Copy, Clone)] pub enum RepeaterType { Cumulate, CatchUp, @@ -84,6 +87,7 @@ pub enum RepeaterType { } #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, Copy, Clone)] pub enum DelayType { All, @@ -91,6 +95,7 @@ pub enum DelayType { } #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, Copy, Clone)] pub enum TimeUnit { Hour, @@ -101,6 +106,7 @@ pub enum TimeUnit { } #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, Copy, Clone)] pub struct Repeater { pub ty: RepeaterType, @@ -109,6 +115,7 @@ pub struct Repeater { } #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug, Copy, Clone)] pub struct Delay { pub ty: DelayType, @@ -118,28 +125,37 @@ pub struct Delay { /// timestamp obejcts #[cfg_attr(test, derive(PartialEq))] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] #[derive(Debug)] pub enum Timestamp<'a> { Active { start: Datetime<'a>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] repeater: Option<&'a str>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] delay: Option<&'a str>, }, Inactive { start: Datetime<'a>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] repeater: Option<&'a str>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] delay: Option<&'a str>, }, ActiveRange { start: Datetime<'a>, end: Datetime<'a>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] repeater: Option<&'a str>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] delay: Option<&'a str>, }, InactiveRange { start: Datetime<'a>, end: Datetime<'a>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] repeater: Option<&'a str>, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] delay: Option<&'a str>, }, Diary(&'a str), diff --git a/src/serde.rs b/src/serde.rs index 2475b6f..7210f26 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,5 +1,5 @@ use indextree::{Arena, NodeId}; -use serde::ser::{SerializeSeq, SerializeStruct, Serializer}; +use serde::ser::{SerializeSeq, Serializer}; use serde::Serialize; use crate::elements::Element; @@ -7,417 +7,27 @@ use crate::org::Org; impl Serialize for Org<'_> { fn serialize(&self, serializer: S) -> Result { - serializer.serialize_newtype_struct( - "Element", - &ElementNode { - node: self.document, - arena: &self.arena, - }, - ) + serializer + .serialize_newtype_struct("ElementNode", &ElementNode::new(self.document, &self.arena)) } } +#[derive(Serialize)] struct ElementNode<'a> { - node: NodeId, - arena: &'a Arena>, + #[serde(flatten)] + element: &'a Element<'a>, + #[serde(skip_serializing_if = "Option::is_none")] + children: Option>, } -impl Serialize for ElementNode<'_> { - #[allow(unused_variables)] - fn serialize(&self, serializer: S) -> Result { - let node = &self.arena[self.node]; - - macro_rules! ser { - ($state:ident, $begin:ident, $end:ident) => { - if cfg!(feature = "extra-serde-info") { - $state.serialize_field("begin", $begin)?; - $state.serialize_field("end", $end)?; - } - }; - ($state:ident, $begin:ident, $end:ident, $contents_begin:ident, $contents_end:ident) => { - if cfg!(feature = "extra-serde-info") { - $state.serialize_field("begin", $begin)?; - $state.serialize_field("end", $end)?; - $state.serialize_field("contents_begin", $contents_begin)?; - $state.serialize_field("contents_end", $contents_end)?; - } - if let Some(first) = node.first_child() { - $state.serialize_field( - "children", - &ElementChildrenNode { - first, - arena: self.arena, - }, - )?; - } - }; - } - - match &node.data { - Element::Root => { - let mut state = serializer.serialize_struct("Element::Root", 2)?; - state.serialize_field("type", "root")?; - if let Some(first) = node.first_child() { - state.serialize_field( - "children", - &ElementChildrenNode { - first, - arena: self.arena, - }, - )?; - } - state.end() - } - Element::Document { - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Document", 2)?; - state.serialize_field("type", "document")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Block { - block, - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Block", 2)?; - state.serialize_field("type", "block")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Section { - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Section", 2)?; - state.serialize_field("type", "section")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Drawer { - drawer, - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Drawer", 2)?; - state.serialize_field("type", "drawer")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::DynBlock { - dyn_block, - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::DynBlock", 2)?; - state.serialize_field("type", "dynamic_block")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::FnDef { - begin, - end, - contents_begin, - contents_end, - fn_def, - } => { - let mut state = serializer.serialize_struct("Element::FnDef", 2)?; - state.serialize_field("type", "footnote_definition")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Headline { - begin, - end, - contents_begin, - contents_end, - headline, - } => { - let mut state = serializer.serialize_struct("Element::Headline", 6)?; - state.serialize_field("type", "headline")?; - state.serialize_field("level", &headline.level)?; - state.serialize_field("title", &headline.title)?; - if let Some(prior) = &headline.priority { - state.serialize_field("priority", prior)?; - } - if let Some(kw) = &headline.keyword { - state.serialize_field("keyword", kw)?; - } - if !headline.tags.is_empty() { - state.serialize_field("tags", &headline.tags)?; - } - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::List { - list, - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::List", 2)?; - state.serialize_field("type", "list")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::ListItem { - list_item, - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::ListItem", 3)?; - state.serialize_field("type", "list_item")?; - state.serialize_field("bullet", list_item.bullet)?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Paragraph { - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Paragraph", 2)?; - state.serialize_field("type", "paragraph")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Clock { clock, begin, end } => { - let mut state = serializer.serialize_struct("Element::Clock", 1)?; - state.serialize_field("type", "clock")?; - ser!(state, begin, end); - state.end() - } - Element::BabelCall { call, begin, end } => { - let mut state = serializer.serialize_struct("Element::BabelCall", 1)?; - state.serialize_field("type", "babel_call")?; - ser!(state, begin, end); - state.end() - } - Element::Cookie { cookie, begin, end } => { - let mut state = serializer.serialize_struct("Element::Cookie", 1)?; - state.serialize_field("type", "cookie")?; - ser!(state, begin, end); - state.end() - } - Element::FnRef { fn_ref, begin, end } => { - let mut state = serializer.serialize_struct("Element::FnRef", 1)?; - state.serialize_field("type", "footnote_reference")?; - ser!(state, begin, end); - state.end() - } - Element::InlineCall { - inline_call, - begin, - end, - } => { - let mut state = serializer.serialize_struct("Element::InlineCall", 1)?; - state.serialize_field("type", "inline_call")?; - ser!(state, begin, end); - state.end() - } - Element::InlineSrc { - inline_src, - begin, - end, - } => { - let mut state = serializer.serialize_struct("Element::InlineSrc", 1)?; - state.serialize_field("type", "inlne_source_block")?; - ser!(state, begin, end); - state.end() - } - Element::Keyword { - keyword, - begin, - end, - } => { - let mut state = serializer.serialize_struct("Element::Keyword", 4)?; - state.serialize_field("type", "keyword")?; - state.serialize_field("key", keyword.key)?; - if let Some(option) = keyword.option { - state.serialize_field("option", option)?; - } - state.serialize_field("value", keyword.value)?; - ser!(state, begin, end); - state.end() - } - Element::Link { link, begin, end } => { - let mut state = serializer.serialize_struct("Element::Link", 3)?; - state.serialize_field("type", "link")?; - state.serialize_field("path", link.path)?; - if let Some(desc) = link.desc { - state.serialize_field("desc", desc)?; - } - ser!(state, begin, end); - state.end() - } - Element::Macros { macros, begin, end } => { - let mut state = serializer.serialize_struct("Element::Macros", 1)?; - state.serialize_field("type", "macros")?; - ser!(state, begin, end); - state.end() - } - Element::Planning { - deadline, - scheduled, - closed, - begin, - end, - } => { - let mut state = serializer.serialize_struct("Element::Planning", 4)?; - state.serialize_field("type", "planning")?; - if let Some(node) = deadline { - state.serialize_field( - "deadline", - &ElementNode { - node: *node, - arena: &self.arena, - }, - )?; - } - if let Some(node) = closed { - state.serialize_field( - "closed", - &ElementNode { - node: *node, - arena: &self.arena, - }, - )?; - } - if let Some(node) = scheduled { - state.serialize_field( - "scheduled", - &ElementNode { - node: *node, - arena: &self.arena, - }, - )?; - } - ser!(state, begin, end); - state.end() - } - Element::Snippet { - begin, - end, - snippet, - } => { - let mut state = serializer.serialize_struct("Element::Snippet", 2)?; - state.serialize_field("type", "snippet")?; - ser!(state, begin, end); - state.end() - } - Element::Text { value, begin, end } => { - let mut state = serializer.serialize_struct("Element::Text", 2)?; - state.serialize_field("type", "text")?; - state.serialize_field("value", value)?; - ser!(state, begin, end); - state.end() - } - Element::Rule { begin, end } => { - let mut state = serializer.serialize_struct("Element::Rule", 1)?; - state.serialize_field("type", "rule")?; - ser!(state, begin, end); - state.end() - } - Element::Timestamp { - begin, - end, - timestamp, - } => { - let mut state = serializer.serialize_struct("Element::Timestamp", 1)?; - state.serialize_field("type", "timestamp")?; - ser!(state, begin, end); - state.end() - } - Element::Bold { - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Bold", 2)?; - state.serialize_field("type", "bold")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Strike { - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Strike", 2)?; - state.serialize_field("type", "strike")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Italic { - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Italic", 2)?; - state.serialize_field("type", "italic")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Underline { - begin, - end, - contents_begin, - contents_end, - } => { - let mut state = serializer.serialize_struct("Element::Underline", 2)?; - state.serialize_field("type", "underline")?; - ser!(state, begin, end, contents_begin, contents_end); - state.end() - } - Element::Code { begin, end, value } => { - let mut state = serializer.serialize_struct("Element::Code", 2)?; - state.serialize_field("type", "code")?; - state.serialize_field("value", value)?; - ser!(state, begin, end); - state.end() - } - Element::Verbatim { begin, end, value } => { - let mut state = serializer.serialize_struct("Element::Verbatim", 2)?; - state.serialize_field("type", "verbatim")?; - state.serialize_field("value", value)?; - ser!(state, begin, end); - state.end() - } - Element::RadioTarget { - radio_target, - begin, - end, - } => { - let mut state = serializer.serialize_struct("Element::RadioTarget", 1)?; - state.serialize_field("type", "radio_target")?; - ser!(state, begin, end); - state.end() - } - Element::Target { target, begin, end } => { - let mut state = serializer.serialize_struct("Element::Target", 1)?; - state.serialize_field("type", "target")?; - ser!(state, begin, end); - state.end() - } +impl<'a> ElementNode<'a> { + fn new(node_id: NodeId, arena: &'a Arena>) -> Self { + let node = &arena[node_id]; + ElementNode { + element: &node.data, + children: node + .first_child() + .map(|first| ElementChildrenNode { first, arena }), } } } @@ -431,10 +41,7 @@ impl Serialize for ElementChildrenNode<'_> { fn serialize(&self, serializer: S) -> Result { let mut seq = serializer.serialize_seq(None)?; for node in self.first.following_siblings(&self.arena) { - seq.serialize_element(&ElementNode { - node, - arena: &self.arena, - })?; + seq.serialize_element(&ElementNode::new(node, &self.arena))?; } seq.end() }