cyberstorm/src/document.rs

74 lines
1.6 KiB
Rust

use std::fmt;
use std::str::FromStr;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum MetaError {
#[error("{0}")]
Parse(toml::de::Error),
#[error("meta data not present")]
NotPresent,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Document<M> {
#[serde(flatten)]
pub meta: M,
pub content: String,
}
impl<M> FromStr for Document<M>
where
M: DeserializeOwned,
{
type Err = MetaError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
parse_str_parts(s).map(|(meta, content)| Self {
meta,
content: content.to_owned(),
})
}
}
impl<M> fmt::Display for Document<M>
where
M: Serialize,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "+++")?;
writeln!(f, "{}", toml::to_string(&self.meta).unwrap())?;
writeln!(f, "\n+++")?;
f.write_str(self.content.as_str())
}
}
impl<M> Document<M>
where
M: DeserializeOwned,
{
pub fn parse_str_meta(s: &str) -> Result<M, MetaError> {
parse_str_parts(s).map(|(meta, _)| meta)
}
}
fn parse_str_parts<M>(s: &str) -> Result<(M, &str), MetaError>
where
M: DeserializeOwned,
{
if let Some(s) = s.strip_prefix("+++") {
let mut parts = s.splitn(2, "\n+++");
match (parts.next(), parts.next()) {
(Some(meta), Some(content)) => toml::from_str(meta)
.map(|meta| (meta, content))
.map_err(MetaError::Parse),
_ => Err(MetaError::NotPresent),
}
} else {
Err(MetaError::NotPresent)
}
}