Add serde support

This commit is contained in:
Isaac Mills 2024-04-10 14:15:16 -04:00
parent 7f245acf66
commit b3339bc4c7
Signed by: fnmain
GPG key ID: B67D7410F33A0F61
4 changed files with 237 additions and 35 deletions

30
Cargo.lock generated
View file

@ -2,12 +2,6 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "autocfg"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.5.0" version = "2.5.0"
@ -41,7 +35,8 @@ dependencies = [
"cosmic-text", "cosmic-text",
"jotdown", "jotdown",
"log", "log",
"range-map", "rangemap",
"serde",
] ]
[[package]] [[package]]
@ -158,15 +153,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.79" version = "1.0.79"
@ -185,20 +171,14 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "range-map"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12a5a2d6c7039059af621472a4389be1215a816df61aa4d531cfe85264aee95f"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "rangemap" name = "rangemap"
version = "1.5.1" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "read-fonts" name = "read-fonts"

View file

@ -9,4 +9,9 @@ edition = "2021"
cosmic-text = "0.11.2" cosmic-text = "0.11.2"
jotdown = { git = "https://git.nations.lol/fnmain/jotdown" } jotdown = { git = "https://git.nations.lol/fnmain/jotdown" }
log = "0.4.21" log = "0.4.21"
range-map = "0.2.0" serde = { version = "1.0.197", features = ["derive"], optional = true }
rangemap = "1.5.1"
[features]
default = ["serde"]
serde = ["dep:serde", "rangemap/serde1"]

View file

@ -1,10 +1,18 @@
use std::borrow::Cow; use std::borrow::Cow;
use cosmic_text::{Attrs, Buffer, Color, Family, FontSystem, Metrics, Shaping, Style, Weight}; use cosmic_text::{
Attrs, AttrsOwned, Buffer, Color, Family, FontSystem, Metrics, Shaping, Style, Weight,
};
use jotdown::{Container, Event, ListKind}; use jotdown::{Container, Event, ListKind};
pub use jotdown; pub use jotdown;
use range_map::RangeMap; use rangemap::RangeMap;
#[cfg(feature = "serde")]
mod serde_suck;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "serde")]
pub use serde_suck::*;
pub struct JotdownBufferIter<'a, T: Iterator<Item = Event<'a>>> { pub struct JotdownBufferIter<'a, T: Iterator<Item = Event<'a>>> {
djot: T, djot: T,
@ -18,13 +26,15 @@ struct JotdownIntoBuffer<'a, 'b, T: Iterator<Item = Event<'a>>> {
indent: &'b mut Vec<Indent>, indent: &'b mut Vec<Indent>,
image_url: Option<Cow<'a, str>>, image_url: Option<Cow<'a, str>>,
link_start: usize, link_start: usize,
urls: Vec<(range_map::Range<usize>, &'a str)>, urls: Vec<(std::ops::Range<usize>, Cow<'a, str>)>,
location: usize, location: usize,
added: bool, added: bool,
} }
#[derive(Default, Clone, Copy)] #[derive(Default, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Indent { pub struct Indent {
#[cfg_attr(feature = "serde", serde(with = "ListKindOption"))]
pub modifier: Option<ListKind>, pub modifier: Option<ListKind>,
pub indent: f32, pub indent: f32,
} }
@ -115,7 +125,7 @@ impl<'a, 'b, T: Iterator<Item = Event<'a>>> Iterator for JotdownIntoBuffer<'a, '
} }
Container::Link(Cow::Borrowed(url), _) => { Container::Link(Cow::Borrowed(url), _) => {
self.urls self.urls
.push((range_map::Range::new(self.link_start, self.location), url)); .push((self.link_start..self.location, Cow::Borrowed(url)));
self.attrs = self.attrs.color(Color::rgb(255, 255, 255)); self.attrs = self.attrs.color(Color::rgb(255, 255, 255));
} }
_ => {} _ => {}
@ -130,12 +140,20 @@ impl<'a, 'b, T: Iterator<Item = Event<'a>>> Iterator for JotdownIntoBuffer<'a, '
} }
} }
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct RichText(
String,
#[cfg_attr(feature = "serde", serde(with = "AttrsSerde"))] AttrsOwned,
);
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct JotdownItem<'a> { pub struct JotdownItem<'a> {
pub indent: Indent, pub indent: Indent,
pub buffer: Vec<(&'a str, Attrs<'static>)>, pub buffer: Vec<RichText>,
#[cfg_attr(feature = "serde", serde(with = "MetricsSerde"))]
pub metrics: Metrics, pub metrics: Metrics,
pub image_url: Option<Cow<'a, str>>, pub image_url: Option<Cow<'a, str>>,
pub url_map: Option<RangeMap<usize, &'a str>>, pub url_map: Option<RangeMap<usize, Cow<'a, str>>>,
} }
impl<'a> JotdownItem<'a> { impl<'a> JotdownItem<'a> {
@ -154,7 +172,7 @@ impl<'a> JotdownItem<'a> {
); );
buffer.set_rich_text( buffer.set_rich_text(
font_system, font_system,
self.buffer.iter().cloned(), self.buffer.iter().map(|r| (r.0.as_str(), r.1.as_attrs())),
Attrs::new().family(Family::SansSerif), Attrs::new().family(Family::SansSerif),
Shaping::Advanced, Shaping::Advanced,
); );
@ -182,7 +200,9 @@ impl<'a, T: Iterator<Item = Event<'a>>> Iterator for JotdownBufferIter<'a, T> {
urls: Vec::new(), urls: Vec::new(),
}; };
let buffer: Vec<(&str, Attrs<'static>)> = (&mut jot).collect(); let buffer = (&mut jot)
.map(|r| RichText(r.0.to_owned(), AttrsOwned::new(r.1)))
.collect::<Vec<_>>();
let image_url = jot.image_url; let image_url = jot.image_url;
let urls = jot.urls; let urls = jot.urls;
let added = jot.added; let added = jot.added;
@ -196,7 +216,9 @@ impl<'a, T: Iterator<Item = Event<'a>>> Iterator for JotdownBufferIter<'a, T> {
url_map: if urls.is_empty() { url_map: if urls.is_empty() {
None None
} else { } else {
RangeMap::try_from_iter(urls.into_iter()).ok() let mut map = RangeMap::new();
map.extend(urls.into_iter());
Some(map)
}, },
buffer, buffer,
image_url, image_url,

195
src/serde_suck.rs Normal file
View file

@ -0,0 +1,195 @@
use cosmic_text::{
Align, Attrs, AttrsOwned, CacheKeyFlags, Color, FamilyOwned, Metrics, Stretch, Style, Weight,
};
use jotdown::{ListKind, OrderedListNumbering, OrderedListStyle};
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Option<ListKind>")]
pub enum ListKindOption {
Some(#[serde(with = "ListKindSerde")] ListKind),
None,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "jotdown::ListKind")]
pub enum ListKindSerde {
Unordered,
Ordered {
numbering: OrderedListNumbering,
style: OrderedListStyle,
start: u64,
},
Task,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "jotdown::OrderedListNumbering")]
pub enum OrderedListNumberingSerde {
Decimal,
AlphaLower,
AlphaUpper,
RomanLower,
RomanUpper,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "jotdown::OrderedListStyle")]
pub enum OrderedListStyleSerde {
Period,
Paren,
ParenParen,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Metrics")]
pub struct MetricsSerde {
pub font_size: f32,
pub line_height: f32,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "FamilyOwned")]
pub enum FamilySerde {
Name(String),
Serif,
SansSerif,
Cursive,
Fantasy,
Monospace,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Stretch")]
pub enum StretchSerde {
UltraCondensed,
ExtraCondensed,
Condensed,
SemiCondensed,
Normal,
SemiExpanded,
Expanded,
ExtraExpanded,
UltraExpanded,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Style")]
pub enum StyleSerde {
Normal,
Italic,
Oblique,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Weight")]
pub struct WeightSerde(pub u16);
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Color")]
pub struct ColorSerde(pub u32);
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Option<Color>")]
pub enum ColorOpt {
Some(#[serde(with = "ColorSerde")] Color),
None,
}
mod cache_key_flags {
use cosmic_text::CacheKeyFlags;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(_: &CacheKeyFlags, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<CacheKeyFlags, D::Error>
where
D: Deserializer<'de>,
{
let _: () = <()>::deserialize(deserializer)?;
Ok(CacheKeyFlags::empty())
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "AttrsOwned")]
pub struct AttrsSerde {
#[serde(with = "ColorOpt")]
pub color_opt: Option<Color>,
#[serde(with = "FamilySerde")]
pub family_owned: FamilyOwned,
#[serde(with = "StretchSerde")]
pub stretch: Stretch,
#[serde(with = "StyleSerde")]
pub style: Style,
#[serde(with = "WeightSerde")]
pub weight: Weight,
pub metadata: usize,
#[serde(with = "cache_key_flags")]
pub cache_key_flags: CacheKeyFlags,
}
impl<'a> From<Attrs<'a>> for AttrsSerde {
fn from(value: Attrs<'a>) -> Self {
Self {
color_opt: value.color_opt,
family_owned: FamilyOwned::new(value.family),
stretch: value.stretch,
style: value.style,
weight: value.weight,
metadata: value.metadata,
cache_key_flags: value.cache_key_flags,
}
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Option<Align>")]
pub enum AlignSerde {
Some(#[serde(with = "AlignRef")] Align),
None,
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[serde(remote = "Align")]
pub enum AlignRef {
Left,
Right,
Center,
Justified,
End,
}
impl AttrsSerde {
pub fn as_attrs(&self) -> Attrs<'_> {
Attrs {
color_opt: self.color_opt,
family: self.family_owned.as_family(),
stretch: self.stretch,
style: self.style,
weight: self.weight,
metadata: self.metadata,
cache_key_flags: CacheKeyFlags::empty(),
}
}
}
impl From<AttrsOwned> for AttrsSerde {
fn from(value: AttrsOwned) -> Self {
Self {
color_opt: value.color_opt,
family_owned: value.family_owned,
stretch: value.stretch,
style: value.style,
weight: value.weight,
metadata: value.metadata,
cache_key_flags: value.cache_key_flags,
}
}
}