summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Tay <sam.chong.tay@gmail.com>2020-07-09 22:24:49 -0700
committerSam Tay <sam.chong.tay@gmail.com>2020-07-10 19:13:49 -0700
commit46e019ddf3b67a8307773edd109aa29d5be1a8b3 (patch)
tree4e7267978a99d3fedc4809223c4b67ed7663b1ff
parent26f57de89cb9b903917f51af4ab5df9e287af1a4 (diff)
Add hidden --print-config-path for debugging
And refactor config to put functions under Config implementation; this seems more Rustic, based on my limited experience looking at other codebases.
-rw-r--r--src/cli.rs18
-rw-r--r--src/config.rs95
-rw-r--r--src/main.rs7
-rw-r--r--src/stackexchange/local_storage.rs4
-rw-r--r--src/tui/app.rs6
5 files changed, 76 insertions, 54 deletions
diff --git a/src/cli.rs b/src/cli.rs
index 92612ea..c8e4b41 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,12 +1,12 @@
use clap::{App, AppSettings, Arg};
-use crate::config;
use crate::config::Config;
use crate::error::Result;
// TODO --add-site (in addition to defaults)
pub struct Opts {
pub list_sites: bool,
+ pub print_config_path: bool,
pub update_sites: bool,
pub set_api_key: Option<String>,
pub query: Option<String>,
@@ -14,7 +14,7 @@ pub struct Opts {
}
pub fn get_opts() -> Result<Opts> {
- let config = config::user_config()?;
+ let config = Config::new()?;
let limit = &config.limit.to_string();
let sites = &config.sites.join(";");
let engine = &config.search_engine.to_string();
@@ -42,6 +42,12 @@ pub fn get_opts() -> Result<Opts> {
.help("Set StackExchange API key"),
)
.arg(
+ Arg::with_name("print-config-path")
+ .long("print-config-path")
+ .help("Print path to config file")
+ .hidden(true),
+ )
+ .arg(
Arg::with_name("site")
.long("site")
.short("s")
@@ -79,7 +85,12 @@ pub fn get_opts() -> Result<Opts> {
Arg::with_name("query")
.multiple(true)
.index(1)
- .required_unless_one(&["list-sites", "update-sites", "set-api-key"]),
+ .required_unless_one(&[
+ "list-sites",
+ "update-sites",
+ "set-api-key",
+ "print-config-path",
+ ]),
)
.arg(
Arg::with_name("search-engine")
@@ -101,6 +112,7 @@ pub fn get_opts() -> Result<Opts> {
};
Ok(Opts {
list_sites: matches.is_present("list-sites"),
+ print_config_path: matches.is_present("print-config-path"),
update_sites: matches.is_present("update-sites"),
set_api_key: matches.value_of("set-api-key").map(String::from),
query: matches
diff --git a/src/config.rs b/src/config.rs
index 49209f6..65907ba 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -56,58 +56,63 @@ impl Default for Config {
}
}
-/// Get user config (writes default if none found)
-pub fn user_config() -> Result<Config> {
- let project = project_dir()?;
- let dir = project.config_dir();
- fs::create_dir_all(&dir)?;
- let filename = config_file_name()?;
+impl Config {
+ /// Get user config (writes default if none found)
+ pub fn new() -> Result<Self> {
+ let project = Self::project_dir()?;
+ let dir = project.config_dir();
+ fs::create_dir_all(&dir)?;
+ let filename = Self::config_file_path()?;
- match utils::open_file(&filename)? {
- None => {
- let def = Config::default();
- write_config(&def)?;
- Ok(def)
+ match utils::open_file(&filename)? {
+ None => {
+ let def = Config::default();
+ def.write()?;
+ Ok(def)
+ }
+ Some(file) => serde_yaml::from_reader(file)
+ .map_err(|_| Error::MalformedFile(filename.clone()))
+ .and_then(|cfg: Config| {
+ if cfg.sites.is_empty() {
+ Err(Error::MalformedFile(filename))
+ } else {
+ Ok(cfg)
+ }
+ }),
}
- Some(file) => serde_yaml::from_reader(file)
- .map_err(|_| Error::MalformedFile(filename.clone()))
- .and_then(|cfg: Config| {
- if cfg.sites.is_empty() {
- Err(Error::MalformedFile(filename))
- } else {
- Ok(cfg)
- }
- }),
}
-}
-pub fn set_api_key(key: String) -> Result<()> {
- let mut cfg = user_config()?;
- cfg.api_key = Some(key);
- write_config(&cfg)
-}
+ // TODO This looks odd when refactoring to associate functions under Config; perhaps this
+ // shouldn't be a CLI opt? Maybe a generic --save-config based on current opts?
+ pub fn set_api_key(key: String) -> Result<()> {
+ let mut cfg = Self::new()?;
+ cfg.api_key = Some(key);
+ cfg.write()
+ }
-/// Get project directory
-pub fn project_dir() -> Result<ProjectDirs> {
- ProjectDirs::from("io", "Sam Tay", "so").ok_or_else(|| Error::ProjectDir)
-}
+ /// Get project directory
+ pub fn project_dir() -> Result<ProjectDirs> {
+ ProjectDirs::from("io", "Sam Tay", "so").ok_or_else(|| Error::ProjectDir)
+ }
-pub fn theme_file_name() -> Result<PathBuf> {
- let name = project_dir()?.config_dir().join("colors.toml");
- if !name.as_path().exists() {
- let mut file = utils::create_file(&name)?;
- file.write_all(include_bytes!("../themes/default.toml"))?;
+ // TODO consider switching to .toml to be consistent with colors.toml
+ pub fn config_file_path() -> Result<PathBuf> {
+ Ok(Self::project_dir()?.config_dir().join("config.yml"))
}
- Ok(name)
-}
-fn write_config(config: &Config) -> Result<()> {
- let filename = config_file_name()?;
- let file = utils::create_file(&filename)?;
- Ok(serde_yaml::to_writer(file, config)?)
-}
+ /// Get theme file path; if it doesn't exist yet, create it with defaults.
+ pub fn theme_file_path() -> Result<PathBuf> {
+ let name = Self::project_dir()?.config_dir().join("colors.toml");
+ if !name.as_path().exists() {
+ let mut file = utils::create_file(&name)?;
+ file.write_all(include_bytes!("../themes/default.toml"))?;
+ }
+ Ok(name)
+ }
-// TODO consider switching to .toml to be consistent with colors.toml
-fn config_file_name() -> Result<PathBuf> {
- Ok(project_dir()?.config_dir().join("config.yml"))
+ fn write(&self) -> Result<()> {
+ let filename = Self::config_file_path()?;
+ let file = utils::create_file(&filename)?;
+ Ok(serde_yaml::to_writer(file, &self)?)
+ }
}
diff --git a/src/main.rs b/src/main.rs
index 2db6c99..48a73a4 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,6 +13,7 @@ use termimad::{CompoundStyle, MadSkin};
use tokio::runtime::Runtime;
use tokio::task;
+use config::Config;
use error::{Error, Result};
use stackexchange::{LocalStorage, Question, Search};
use tui::markdown::Markdown;
@@ -49,7 +50,11 @@ async fn run(skin: &mut MadSkin) -> Result<Option<Vec<Question<Markdown>>>> {
let ls = LocalStorage::new(opts.update_sites).await?;
if let Some(key) = opts.set_api_key {
- config::set_api_key(key)?;
+ Config::set_api_key(key)?;
+ }
+
+ if opts.print_config_path {
+ println!("{}", Config::config_file_path()?.display());
}
if opts.list_sites {
diff --git a/src/stackexchange/local_storage.rs b/src/stackexchange/local_storage.rs
index 8d009f8..d5adeb2 100644
--- a/src/stackexchange/local_storage.rs
+++ b/src/stackexchange/local_storage.rs
@@ -2,7 +2,7 @@ use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
-use crate::config::project_dir;
+use crate::config::Config;
use crate::error::{Error, Result};
use crate::utils;
@@ -40,7 +40,7 @@ impl LocalStorage {
}
pub async fn new(update: bool) -> Result<Self> {
- let project = project_dir()?;
+ let project = Config::project_dir()?;
let dir = project.cache_dir();
fs::create_dir_all(&dir)?;
let sites_filename = dir.join("sites.json");
diff --git a/src/tui/app.rs b/src/tui/app.rs
index 2763663..bdd4665 100644
--- a/src/tui/app.rs
+++ b/src/tui/app.rs
@@ -15,7 +15,7 @@ use super::views::{
LayoutView, ListView, MdView, Name, Vimable, NAME_ANSWER_LIST, NAME_ANSWER_VIEW,
NAME_QUESTION_LIST, NAME_QUESTION_VIEW,
};
-use crate::config;
+use crate::config::Config;
use crate::error::Result;
use crate::stackexchange::{Answer, Question};
@@ -23,7 +23,7 @@ pub const NAME_HELP_VIEW: &str = "help_view";
pub fn run(qs: Vec<Question<Markdown>>) -> Result<()> {
let mut siv = cursive::default();
- siv.load_theme_file(config::theme_file_name()?).unwrap(); // TODO dont unwrap
+ siv.load_theme_file(Config::theme_file_path()?).unwrap(); // TODO dont unwrap
let question_map: HashMap<u32, Question<Markdown>> =
qs.clone().into_iter().map(|q| (q.id, q)).collect();
@@ -76,7 +76,7 @@ pub fn run(qs: Vec<Question<Markdown>>) -> Result<()> {
});
// Reload theme
siv.add_global_callback(Event::CtrlChar('r'), |s| {
- s.load_theme_file(config::theme_file_name().unwrap())
+ s.load_theme_file(Config::theme_file_path().unwrap())
.unwrap()
});
siv.run();