74 lines
1.6 KiB
Rust
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)
|
|
}
|
|
}
|