|
|
@ -45,6 +45,13 @@ pub struct Org { |
|
|
|
date: String, |
|
|
|
date: String, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Data used to dump the content into disk
|
|
|
|
|
|
|
|
struct Dump<'a> { |
|
|
|
|
|
|
|
fp: File, |
|
|
|
|
|
|
|
record: &'a Data, |
|
|
|
|
|
|
|
path: PathBuf, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Simple macro to recursively walk through html5ever nodes
|
|
|
|
/// Simple macro to recursively walk through html5ever nodes
|
|
|
|
macro_rules! keep_going { |
|
|
|
macro_rules! keep_going { |
|
|
|
($source:ident, $target:ident) => { |
|
|
|
($source:ident, $target:ident) => { |
|
|
@ -139,11 +146,12 @@ impl Org { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Open the destination Org file; if it doesn't exist, create it and
|
|
|
|
/// Adds information about the attachments
|
|
|
|
/// append the header.
|
|
|
|
fn attachments(&self, fp: &mut File, record: &Data) {} |
|
|
|
fn open_file(&self) -> File { |
|
|
|
|
|
|
|
|
|
|
|
fn start_org<'a>(&self, record: &'a Data) -> Dump<'a> { |
|
|
|
let org_file = self.path.join(&self.filename); |
|
|
|
let org_file = self.path.join(&self.filename); |
|
|
|
OpenOptions::new() |
|
|
|
let fp = OpenOptions::new() |
|
|
|
.write(true) |
|
|
|
.write(true) |
|
|
|
.append(true) |
|
|
|
.append(true) |
|
|
|
.open(&org_file) |
|
|
|
.open(&org_file) |
|
|
@ -164,46 +172,61 @@ impl Org { |
|
|
|
fp |
|
|
|
fp |
|
|
|
}) |
|
|
|
}) |
|
|
|
.unwrap() |
|
|
|
.unwrap() |
|
|
|
}) |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Dump { |
|
|
|
|
|
|
|
fp, |
|
|
|
|
|
|
|
record, |
|
|
|
|
|
|
|
path: self.path, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Create the Org header that starts this element
|
|
|
|
impl Dump<'_> { |
|
|
|
fn org_title(&self, fp: &mut File, record: &Data) { |
|
|
|
/// The initial header for the content
|
|
|
|
let title = format!("* {user}/{id}", user = record.account, id = record.id); |
|
|
|
fn intro(mut self) -> Self { |
|
|
|
fp.write_all(title.as_bytes()).unwrap(); |
|
|
|
let title = format!( |
|
|
|
fp.write_all("\n".as_bytes()).unwrap(); |
|
|
|
"* {user}/{id}", |
|
|
|
|
|
|
|
user = &self.record.account, |
|
|
|
|
|
|
|
id = &self.record.id |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
self.fp.write_all(title.as_bytes()).unwrap(); |
|
|
|
|
|
|
|
self.fp.write_all("\n".as_bytes()).unwrap(); |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Adds the title (content warning) of the content
|
|
|
|
/// If the content has a title (content warning), add it
|
|
|
|
fn title(&self, fp: &mut File, record: &Data) { |
|
|
|
fn title(mut self) -> Self { |
|
|
|
if !record.title.is_empty() { |
|
|
|
if !self.record.title.is_empty() { |
|
|
|
let warning = format!(" ({})", &record.title); |
|
|
|
let warning = format!(" ({})", &self.record.title); |
|
|
|
fp.write_all(warning.as_bytes()).unwrap(); |
|
|
|
self.fp.write_all(warning.as_bytes()).unwrap(); |
|
|
|
Org::prologue(fp); |
|
|
|
Dump::prologue(&mut self.fp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Adds the main text content
|
|
|
|
/// The main body of the content
|
|
|
|
fn text(&self, fp: &mut File, record: &Data) { |
|
|
|
fn text(mut self) -> Self { |
|
|
|
let dom = parse_document(RcDom::default(), Default::default()) |
|
|
|
let dom = parse_document(RcDom::default(), Default::default()) |
|
|
|
.from_utf8() |
|
|
|
.from_utf8() |
|
|
|
.read_from(&mut record.text.as_bytes()) |
|
|
|
.read_from(&mut self.record.text.as_bytes()) |
|
|
|
.unwrap(); |
|
|
|
.unwrap(); |
|
|
|
let mut result = String::new(); |
|
|
|
let mut result = String::new(); |
|
|
|
result.push_str(" "); // initial identantion
|
|
|
|
result.push_str(" "); // initial identantion
|
|
|
|
walk(&dom.document, &mut result); |
|
|
|
walk(&dom.document, &mut result); |
|
|
|
fp.write_all(" ".as_bytes()).unwrap(); // initial indentantion
|
|
|
|
self.fp.write_all(" ".as_bytes()).unwrap(); // initial indentantion
|
|
|
|
fp.write_all(result.trim().as_bytes()).unwrap(); |
|
|
|
self.fp.write_all(result.trim().as_bytes()).unwrap(); |
|
|
|
Org::prologue(fp); |
|
|
|
Dump::prologue(&mut self.fp); |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Adds information about the attachments
|
|
|
|
/// Add the final attachments
|
|
|
|
fn attachments(&self, fp: &mut File, record: &Data) { |
|
|
|
fn attachments(mut self) -> Self { |
|
|
|
if !record.attachments.is_empty() { |
|
|
|
if !self.record.attachments.is_empty() { |
|
|
|
fp.write_all(" Attachments:\n".as_bytes()).unwrap(); |
|
|
|
self.fp.write_all(" Attachments:\n".as_bytes()).unwrap(); |
|
|
|
for attachment in record.attachments.iter() { |
|
|
|
for attachment in self.record.attachments.iter() { |
|
|
|
let filename = attachment.filename().to_string(); |
|
|
|
let filename = attachment.filename().to_string(); |
|
|
|
let storage_name = format!("{}-{}", &record.id, &filename); |
|
|
|
let storage_name = format!("{}-{}", &self.record.id, &filename); |
|
|
|
let in_storage = self.path.join(&storage_name); |
|
|
|
let in_storage = self.path.join(&storage_name); |
|
|
|
log::debug!("Saving attachment in {}", in_storage.to_string_lossy()); |
|
|
|
log::debug!("Saving attachment in {}", in_storage.to_string_lossy()); |
|
|
|
let mut target = File::create(&in_storage).unwrap(); |
|
|
|
let mut target = File::create(&in_storage).unwrap(); |
|
|
@ -216,24 +239,31 @@ impl Org { |
|
|
|
|
|
|
|
|
|
|
|
let attachment_info = |
|
|
|
let attachment_info = |
|
|
|
format!(" - [[{}][{}]\n", in_storage.to_string_lossy(), filename); |
|
|
|
format!(" - [[{}][{}]\n", in_storage.to_string_lossy(), filename); |
|
|
|
fp.write_all(attachment_info.as_bytes()).unwrap(); |
|
|
|
self.fp.write_all(attachment_info.as_bytes()).unwrap(); |
|
|
|
} |
|
|
|
} |
|
|
|
Org::prologue(fp); |
|
|
|
Dump::prologue(&mut self.fp); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
self |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Prologue: The end of the content
|
|
|
|
/// Prologue: The end of the content
|
|
|
|
fn prologue(fp: &mut File) { |
|
|
|
fn prologue(fp: &mut File) { |
|
|
|
fp.write_all("\n\n".as_bytes()).unwrap(); |
|
|
|
fp.write_all("\n\n".as_bytes()).unwrap(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// Done: Complete the data
|
|
|
|
|
|
|
|
fn done(self) { |
|
|
|
|
|
|
|
// because we have all the prologues, we don't need to do anything else.
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Storage for Org { |
|
|
|
impl Storage for Org { |
|
|
|
fn save(&self, record: &Data) { |
|
|
|
fn save(&self, record: &Data) { |
|
|
|
let mut fp = self.open_file(); |
|
|
|
self.start_org(record) |
|
|
|
self.org_title(&mut fp, record); |
|
|
|
.intro() |
|
|
|
self.title(&mut fp, record); |
|
|
|
.title() |
|
|
|
self.text(&mut fp, record); |
|
|
|
.text() |
|
|
|
self.attachments(&mut fp, record); |
|
|
|
.attachments() |
|
|
|
|
|
|
|
.done(); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|