use crate::data::{
Element, ElementQuery, FromVimwikiElement, GqlPageFilter,
GraphqlDatabaseError, Page, PageQuery, Region,
};
use entity::*;
use entity_async_graphql::*;
use std::collections::HashMap;
use vimwiki::{self as v, Located};
#[simple_ent]
#[derive(EntFilter)]
pub struct CodeBlock {
#[ent(field(graphql(filter_untyped)))]
region: Region,
language: Option<String>,
lines: Vec<String>,
#[ent(field(graphql(filter_untyped)))]
metadata: HashMap<String, String>,
#[ent(edge)]
page: Page,
#[ent(edge(policy = "shallow", wrap, graphql(filter_untyped)))]
parent: Option<Element>,
}
#[async_graphql::Object]
impl CodeBlock {
#[graphql(name = "region")]
async fn gql_region(&self) -> &Region {
self.region()
}
#[graphql(name = "lines")]
async fn gql_lines(&self) -> &[String] {
self.lines()
}
#[graphql(name = "text")]
async fn gql_text(&self) -> String {
self.lines().join(" ")
}
#[graphql(name = "language")]
async fn gql_language(&self) -> Option<String> {
self.language()
.as_deref()
.or_else(|| {
self.metadata
.get("class")
.and_then(|x| x.strip_prefix("brush:"))
})
.map(|x| x.trim().to_string())
}
#[graphql(name = "metadata_for_key")]
async fn gql_metadata_for_key(&self, key: String) -> Option<&String> {
self.metadata().get(&key)
}
#[graphql(name = "metadata")]
async fn gql_metadata(&self) -> &HashMap<String, String> {
self.metadata()
}
#[graphql(name = "page")]
async fn gql_page(&self) -> async_graphql::Result<Page> {
self.load_page()
.map_err(|x| async_graphql::Error::new(x.to_string()))
}
#[graphql(name = "parent")]
async fn gql_parent(&self) -> async_graphql::Result<Option<Element>> {
self.load_parent()
.map_err(|x| async_graphql::Error::new(x.to_string()))
}
}
impl<'a> FromVimwikiElement<'a> for CodeBlock {
type Element = Located<v::CodeBlock<'a>>;
fn from_vimwiki_element(
page_id: Id,
parent_id: Option<Id>,
element: Self::Element,
) -> Result<Self, GraphqlDatabaseError> {
let region = Region::from(element.region());
let language = element
.as_inner()
.language
.as_ref()
.map(ToString::to_string);
let lines = element
.as_inner()
.lines
.iter()
.map(ToString::to_string)
.collect();
let metadata = element
.into_inner()
.metadata
.into_iter()
.map(|(k, v)| (k.to_string(), v.to_string()))
.collect();
GraphqlDatabaseError::wrap(
Self::build()
.region(region)
.language(language)
.lines(lines)
.metadata(metadata)
.page(page_id)
.parent(parent_id)
.finish_and_commit(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use entity_inmemory::InmemoryDatabase;
use vimwiki::macros::*;
#[test]
fn should_fully_populate_from_vimwiki_element() {
global::with_db(InmemoryDatabase::default(), || {
let element = vimwiki_code_block! {r#"
{{{c++ prop="text"
First line of text
Second line of text
}}}
"#};
let region = Region::from(element.region());
let ent = CodeBlock::from_vimwiki_element(999, Some(123), element)
.expect("Failed to convert from element");
assert_eq!(
ent.lines(),
&[
"First line of text".to_string(),
"Second line of text".to_string()
],
);
assert_eq!(ent.language(), &Some("c++".to_string()));
let mut metadata = HashMap::new();
metadata.insert("prop".to_string(), "text".to_string());
assert_eq!(ent.metadata(), &metadata);
assert_eq!(ent.region(), ®ion);
assert_eq!(ent.page_id(), 999);
assert_eq!(ent.parent_id(), Some(123));
});
}
}