diff --git a/src/args/mod.rs b/src/args/mod.rs index 000a27f..e3cfa40 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -102,6 +102,15 @@ pub fn parse() -> Result { StorageType::try_from(storage)?, )) } + ("remove", Some(remove_args)) => { + let storage = remove_args + .subcommand_name() + .ok_or(ParsingError::UnknownCommand)?; + Ok(Command::remove_storage( + account_name.into(), + StorageType::try_from(storage)?, + )) + } _ => unimplemented!(), }, ("fetch", _) => Ok(Command::fetch(account_name.into())), diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 073453a..ff18538 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -33,6 +33,8 @@ use crate::config::Configurable; use crate::storage::data::Data; use crate::storage::markdown::config::MarkdownConfig; use crate::storage::markdown::storage::Markdown; +use crate::storage::org::config::OrgConfig; +use crate::storage::org::storage::Org; use crate::storage::storage::Storage; type CommandResult = Result<(), CommandError>; @@ -75,6 +77,9 @@ pub enum Command { /// Add a storage in an account. AddStorage(String, StorageType), + /// Remove a storage in an account. + RemoveStorage(String, StorageType), + /// Fetch favourites from all accounts. FetchAll, @@ -98,6 +103,10 @@ impl Command { Command::AddStorage(account.into(), storage) } + pub fn remove_storage(account: &str, storage: StorageType) -> Self { + Command::RemoveStorage(account.into(), storage) + } + pub fn fetch_all() -> Self { Command::FetchAll } @@ -118,6 +127,9 @@ impl Command { Command::AddStorage(account, storage) => { add_storage(account, storage) } + Command::RemoveStorage(account, storage) => { + remove_storage(account, storage) + } Command::FetchAll => fetch_all(), Command::Fetch(account) => fetch_account(account), Command::Sync(account) => sync_account(account), @@ -158,6 +170,25 @@ fn add_storage(account: &str, storage: &StorageType) -> CommandResult { let storage_config = MarkdownConfig::config()?; config.set_storage_markdown(account, storage_config); } + StorageType::Org => { + let storage_config = OrgConfig::config()?; + config.set_storage_org(account, storage_config); + } + _ => unimplemented!(), + } + config.save()?; + Ok(()) +} + +fn remove_storage(account: &str, storage: &StorageType) -> CommandResult { + let mut config = Config::open()?; + match storage { + StorageType::Markdown => { + config.remove_storage_markdown(account); + } + StorageType::Org => { + config.remove_storage_org(account); + } _ => unimplemented!(), } config.save()?; @@ -203,6 +234,10 @@ fn fetch_account_favourites(account: &AccountConfig) -> Option { Some(config) => Some(Markdown::new(&config)), None => None, }; + let org_storage = match account.org() { + Some(config) => Some(Org::new(&config)), + None => None, + }; for toot in client.favourites().ok()?.items_iter() { if toot.id == top { break; @@ -218,6 +253,10 @@ fn fetch_account_favourites(account: &AccountConfig) -> Option { if let Some(storage) = markdown_storage.as_ref() { storage.save(&conversion); } + + if let Some(storage) = org_storage.as_ref() { + storage.save(&conversion); + } } most_recent } diff --git a/src/config/account.rs b/src/config/account.rs index 48b78f4..9300185 100644 --- a/src/config/account.rs +++ b/src/config/account.rs @@ -24,6 +24,7 @@ use serde_derive::Serialize; use super::favourite::Favourite; use crate::storage::markdown::config::MarkdownConfig; +use crate::storage::org::config::OrgConfig; /// Account configuration #[derive(Serialize, Deserialize, Debug)] @@ -32,7 +33,7 @@ pub struct AccountConfig { mastodon: Data, markdown: Option, // joplin: Option, - // org: Option, + org: Option, } impl AccountConfig { @@ -43,6 +44,7 @@ impl AccountConfig { mastodon: configuration, favourite: Favourite::default(), markdown: None, + org: None, } } @@ -68,9 +70,31 @@ impl AccountConfig { self.markdown = Some(config); } + /// Remove the Markdown configuration. + #[logfn_inputs(Trace)] + pub fn remove_markdown(&mut self) { + self.markdown = None; + } + /// Return the Markdown configuration. #[logfn(Trace)] pub fn markdown(&self) -> &Option { &self.markdown } + + /// Set the Org configuration. + #[logfn_inputs(Trace)] + pub fn set_org(&mut self, config: OrgConfig) { + self.org = Some(config); + } + + /// Remove the Org configuration. + pub fn remove_org(&mut self) { + self.org = None; + } + + /// Return the Org configuration. + pub fn org(&self) -> &Option { + &self.org + } } diff --git a/src/config/config.rs b/src/config/config.rs index 0fb5086..a018842 100644 --- a/src/config/config.rs +++ b/src/config/config.rs @@ -32,6 +32,7 @@ use serde_derive::Serialize; use super::account::AccountConfig; use crate::config::errors::ConfigError; use crate::storage::markdown::config::MarkdownConfig; +use crate::storage::org::config::OrgConfig; /// The main configuration #[derive(Serialize, Deserialize, Debug)] @@ -88,6 +89,33 @@ impl Config { } } + /// Remove the Markdown storage from the account. + #[logfn_inputs(Trace)] + pub fn remove_storage_markdown(&mut self, account: &str) { + match self.0.get_mut(account.into()) { + Some(account_config) => account_config.remove_markdown(), + None => {} + } + } + + /// Set the configuration for the Org storage. + #[logfn_inputs(Trace)] + pub fn set_storage_org(&mut self, account: &str, config: OrgConfig) { + match self.0.get_mut(account.into()) { + Some(account_config) => account_config.set_org(config), + None => {} + } + } + + /// Remove the Org storage. + #[logfn_inputs(Trace)] + pub fn remove_storage_org(&mut self, account: &str) { + match self.0.get_mut(account.into()) { + Some(account_config) => account_config.remove_org(), + None => {} + } + } + /// Set the a last seen favourite for the account #[logfn_inputs(Trace)] pub fn set_new_favourite(&mut self, account: &str, favourite: &str) { diff --git a/src/storage/markdown/config.rs b/src/storage/markdown/config.rs index bf1349c..efc8e21 100644 --- a/src/storage/markdown/config.rs +++ b/src/storage/markdown/config.rs @@ -18,13 +18,14 @@ use std::io::Write; +use log_derive::logfn; use serde_derive::Deserialize; use serde_derive::Serialize; use crate::config::errors::ConfigError; use crate::config::Configurable; -/// Configuration for the Filesystem backend +/// Configuration for the Markdown backend #[derive(Serialize, Deserialize, Debug)] pub struct MarkdownConfig { /// Path where files will be stored. @@ -32,6 +33,7 @@ pub struct MarkdownConfig { } impl Configurable for MarkdownConfig { + #[logfn(Trace)] fn config() -> Result { print!("Base path for your files: "); std::io::stdout().flush().expect("Failed to flush stdout!"); @@ -39,7 +41,6 @@ impl Configurable for MarkdownConfig { let mut path = String::new(); std::io::stdin().read_line(&mut path)?; let fullpath = shellexpand::full(path.trim())?; - log::debug!("Full path: {:?}", fullpath); Ok(Self { path: fullpath.into(), }) diff --git a/src/storage/mod.rs b/src/storage/mod.rs index eccc3ef..8e59b1e 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -20,6 +20,6 @@ pub mod attachment; pub mod data; pub mod helpers; // pub mod joplin; -// pub mod org; pub mod markdown; +pub mod org; pub mod storage; diff --git a/src/storage/org/config.rs b/src/storage/org/config.rs new file mode 100644 index 0000000..c83cf9b --- /dev/null +++ b/src/storage/org/config.rs @@ -0,0 +1,48 @@ +/* + DOWNFAV - Download Favourites + Copyright (C) 2020 Julio Biason + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +use std::io::Write; + +use log_derive::logfn; +use serde_derive::Deserialize; +use serde_derive::Serialize; + +use crate::config::errors::ConfigError; +use crate::config::Configurable; + +/// Configuration for the Org backend +#[derive(Serialize, Deserialize, Debug)] +pub struct OrgConfig { + pub path: String, +} + +impl Configurable for OrgConfig { + #[logfn(Trace)] + fn config() -> Result { + print!("Base path for Org files: "); + std::io::stdout().flush().expect("Failed to flush stdout!"); + + let mut path = String::new(); + std::io::stdin().read_line(&mut path)?; + let fullpath = shellexpand::full(path.trim())?; + log::debug!("Full path: {:?}", fullpath); + Ok(Self { + path: fullpath.into(), + }) + } +} diff --git a/src/storage/org/mod.rs b/src/storage/org/mod.rs new file mode 100644 index 0000000..d5c5ab2 --- /dev/null +++ b/src/storage/org/mod.rs @@ -0,0 +1,2 @@ +pub mod config; +pub mod storage; diff --git a/src/storage/org.rs b/src/storage/org/storage.rs similarity index 91% rename from src/storage/org.rs rename to src/storage/org/storage.rs index 369a3ab..b66930d 100644 --- a/src/storage/org.rs +++ b/src/storage/org/storage.rs @@ -31,7 +31,7 @@ use markup5ever_rcdom::Handle; use markup5ever_rcdom::NodeData; use markup5ever_rcdom::RcDom; -use crate::config::OrgConfig; +use super::config::OrgConfig; use crate::storage::data::Data; use crate::storage::storage::Storage; @@ -132,16 +132,22 @@ fn walk(input: &Handle, result: &mut String) { } impl Org { - pub(crate) fn new_from_config(config: &OrgConfig) -> Org { + pub(crate) fn new(config: &OrgConfig) -> Org { let now = Utc::now(); - let filename = format!("{:>04}{:>02}{:>02}.org", now.year(), now.month(), now.day()); - let date = format!("{:>04}-{:>02}-{:>02}", now.year(), now.month(), now.day()); - let full_path = Path::new(&config.location).join(&filename); + let filename = format!( + "{:>04}{:>02}{:>02}.org", + now.year(), + now.month(), + now.day() + ); + let date = + format!("{:>04}-{:>02}-{:>02}", now.year(), now.month(), now.day()); + let full_path = Path::new(&config.path).join(&filename); log::debug!("Org file: {}", full_path.to_string_lossy()); Org { - path: Path::new(&config.location).to_path_buf(), - filename: filename, + path: Path::new(&config.path).to_path_buf(), + filename, date, } } @@ -164,8 +170,10 @@ impl Org { .create(true) .open(&org_file) .map(|mut fp| { - let text = - format!("#+title: Favourites from {date}\n\n", date = &self.date); + let text = format!( + "#+title: Favourites from {date}\n\n", + date = &self.date + ); fp.write_all(text.as_bytes()).unwrap(); fp }) @@ -226,7 +234,10 @@ impl Dump<'_> { let filename = attachment.filename().to_string(); let storage_name = format!("{}-{}", &self.record.id, &filename); 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(); log::debug!( "Downloading attachment {} as {}",