use crate::config::LocalLinkKind;
use crate::config::TMPL_VAR_FM_;
use crate::config::TMPL_VAR_FM_ALL;
use crate::config::TMPL_VAR_FM_FILENAME_SYNC;
use crate::config::TMPL_VAR_FM_NO_FILENAME_SYNC;
use crate::config::TMPL_VAR_FM_SCHEME;
use crate::content::Content;
use crate::context::Context;
use crate::error::NoteError;
use crate::html_renderer::HtmlRenderer;
use crate::note::Note;
use crate::settings::SchemeSource;
use crate::settings::Settings;
use crate::settings::SETTINGS;
use crate::template::TemplateKind;
use parking_lot::RwLockUpgradableReadGuard;
use std::path::Path;
use std::path::PathBuf;
use tera::Value;
#[derive(Debug, Clone)]
pub struct WorkflowBuilder<W> {
input: W,
}
#[derive(Debug, Clone)]
pub struct SyncFilename<'a> {
path: &'a Path,
}
#[derive(Debug, Clone)]
pub struct SyncFilenameOrCreateNew<'a, T, F> {
scheme_source: SchemeSource<'a>,
path: &'a Path,
clipboards: Vec<&'a T>,
tk_filter: F,
html_export: Option<(&'a Path, LocalLinkKind)>,
force_lang: Option<&'a str>,
}
impl<'a> WorkflowBuilder<SyncFilename<'a>> {
pub fn new(path: &'a Path) -> Self {
Self {
input: SyncFilename { path },
}
}
pub fn upgrade<T: Content, F: Fn(TemplateKind) -> TemplateKind>(
self,
scheme_new_default: &'a str,
clipboards: Vec<&'a T>,
tk_filter: F,
) -> WorkflowBuilder<SyncFilenameOrCreateNew<'a, T, F>> {
WorkflowBuilder {
input: SyncFilenameOrCreateNew {
scheme_source: SchemeSource::SchemeNewDefault(scheme_new_default),
path: self.input.path,
clipboards,
tk_filter,
html_export: None,
force_lang: None,
},
}
}
pub fn build(self) -> Workflow<SyncFilename<'a>> {
Workflow { input: self.input }
}
}
impl<'a, T: Content, F: Fn(TemplateKind) -> TemplateKind>
WorkflowBuilder<SyncFilenameOrCreateNew<'a, T, F>>
{
pub fn html_export(&mut self, path: &'a Path, local_link_kind: LocalLinkKind) {
self.input.html_export = Some((path, local_link_kind));
}
pub fn force_scheme(&mut self, scheme: &'a str) {
self.input.scheme_source = SchemeSource::Force(scheme);
}
pub fn force_lang(&mut self, force_lang: &'a str) {
self.input.force_lang = Some(force_lang);
}
pub fn build(self) -> Workflow<SyncFilenameOrCreateNew<'a, T, F>> {
Workflow { input: self.input }
}
}
#[derive(Debug, Clone)]
pub struct Workflow<W> {
input: W,
}
impl Workflow<SyncFilename<'_>> {
pub fn run<T: Content>(self) -> Result<PathBuf, NoteError> {
let mut settings = SETTINGS.upgradable_read();
let context = Context::from(self.input.path)?;
let content = <T>::open(self.input.path).unwrap_or_default();
let mut n = Note::from_existing_content(context, content, TemplateKind::SyncFilename)?;
synchronize_filename(&mut settings, &mut n)?;
Ok(n.rendered_filename)
}
}
impl<T: Content, F: Fn(TemplateKind) -> TemplateKind> Workflow<SyncFilenameOrCreateNew<'_, T, F>> {
pub fn run(self) -> Result<PathBuf, NoteError> {
let mut settings = SETTINGS.upgradable_read();
settings.with_upgraded(|settings| {
settings.update(self.input.scheme_source, self.input.force_lang)
})?;
let context = Context::from(self.input.path)?;
let (template_kind, content) = TemplateKind::from(self.input.path);
let template_kind = (self.input.tk_filter)(template_kind);
let n = match template_kind {
TemplateKind::FromDir | TemplateKind::AnnotateFile => {
let context = context
.insert_front_matter_and_raw_text_from_existing_content(&self.input.clipboards)?
.set_state_ready_for_content_template();
let mut n = Note::from_content_template(context, template_kind)?;
n.render_filename(template_kind)?;
n.set_next_unused_rendered_filename()?;
n.save()?;
n
}
TemplateKind::FromTextFile => {
let content: T = content.unwrap();
debug_assert!(&content.header().is_empty());
debug_assert!(!&content.body().is_empty());
let context = context
.insert_front_matter_and_raw_text_from_existing_content(&self.input.clipboards)?
.insert_front_matter_and_raw_text_from_existing_content(&vec![&content])?;
let context = context.set_state_ready_for_content_template();
let mut n = Note::from_content_template(context, TemplateKind::FromTextFile)?;
n.render_filename(template_kind)?;
let context_path = n.context.get_path().to_owned();
n.set_next_unused_rendered_filename_or(&context_path)?;
n.save_and_delete_from(&context_path)?;
n
}
TemplateKind::SyncFilename => {
let mut n = Note::from_existing_content(
context,
content.unwrap(),
TemplateKind::SyncFilename,
)?;
synchronize_filename(&mut settings, &mut n)?;
n
}
TemplateKind::None => {
Note::from_existing_content(context, content.unwrap(), template_kind)?
}
};
let mut n = n;
if n.rendered_filename == PathBuf::new() {
n.rendered_filename = n.context.get_path().to_owned();
}
if let Some((export_dir, local_link_kind)) = self.input.html_export {
HtmlRenderer::save_exporter_page(
&n.rendered_filename,
n.content,
export_dir,
local_link_kind,
)?;
}
Ok(n.rendered_filename)
}
}
fn synchronize_filename<T: Content>(
settings: &mut RwLockUpgradableReadGuard<Settings>,
note: &mut Note<T>,
) -> Result<(), NoteError> {
let no_filename_sync = match (
note.context
.get(TMPL_VAR_FM_ALL)
.and_then(|v| v.get(TMPL_VAR_FM_FILENAME_SYNC)),
note.context
.get(TMPL_VAR_FM_ALL)
.and_then(|v| v.get(TMPL_VAR_FM_NO_FILENAME_SYNC)),
) {
(None, None) => false,
(None, Some(Value::Bool(nsync))) => *nsync,
(None, Some(_)) => true,
(Some(Value::Bool(sync)), None) => !*sync,
_ => false,
};
if no_filename_sync {
log::info!(
"Filename synchronisation disabled with the front matter field: `{}: {}`",
TMPL_VAR_FM_FILENAME_SYNC.trim_start_matches(TMPL_VAR_FM_),
!no_filename_sync
);
return Ok(());
}
match note
.context
.get(TMPL_VAR_FM_ALL)
.and_then(|v| v.get(TMPL_VAR_FM_SCHEME))
{
Some(Value::String(s)) if !s.is_empty() => {
settings
.with_upgraded(|settings| settings.update_current_scheme(SchemeSource::Force(s)))?;
log::info!("Switch to scheme `{}` as indicated in front matter", s);
}
Some(Value::String(_)) | None => {
settings.with_upgraded(|settings| {
settings.update_current_scheme(SchemeSource::SchemeSyncDefault)
})?;
}
Some(_) => {
return Err(NoteError::FrontMatterFieldIsNotString {
field_name: TMPL_VAR_FM_SCHEME.to_string(),
});
}
};
note.render_filename(TemplateKind::SyncFilename)?;
let path = note.context.get_path().to_owned();
note.set_next_unused_rendered_filename_or(&path)?;
note.rename_file_from(note.context.get_path())?;
Ok(())
}