aboutsummaryrefslogtreecommitdiffstats
path: root/atuin-client/src/settings.rs
diff options
context:
space:
mode:
authorEllie Huxtable <e@elm.sh>2021-04-20 21:53:07 +0100
committerGitHub <noreply@github.com>2021-04-20 20:53:07 +0000
commita21737e2b7f8d1e426726bdd7536033f299d476a (patch)
treee940afdff9c145d25d9a2895fd44a77d70719a2e /atuin-client/src/settings.rs
parentSwitch to Warp + SQLx, use async, switch to Rust stable (#36) (diff)
downloadatuin-a21737e2b7f8d1e426726bdd7536033f299d476a.zip
Use cargo workspaces (#37)
* Switch to Cargo workspaces Breaking things into "client", "server" and "common" makes managing the codebase much easier! client - anything running on a user's machine for adding history server - handles storing/syncing history and running a HTTP server common - request/response API definitions, common utils, etc * Update dockerfile
Diffstat (limited to 'atuin-client/src/settings.rs')
-rw-r--r--atuin-client/src/settings.rs149
1 files changed, 149 insertions, 0 deletions
diff --git a/atuin-client/src/settings.rs b/atuin-client/src/settings.rs
new file mode 100644
index 00000000..e28963c0
--- /dev/null
+++ b/atuin-client/src/settings.rs
@@ -0,0 +1,149 @@
+use std::fs::{create_dir_all, File};
+use std::io::prelude::*;
+use std::path::{Path, PathBuf};
+
+use chrono::prelude::*;
+use chrono::Utc;
+use config::{Config, Environment, File as ConfigFile};
+use directories::ProjectDirs;
+use eyre::{eyre, Result};
+use parse_duration::parse;
+
+pub const HISTORY_PAGE_SIZE: i64 = 100;
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct Settings {
+ pub dialect: String,
+ pub auto_sync: bool,
+ pub sync_address: String,
+ pub sync_frequency: String,
+ pub db_path: String,
+ pub key_path: String,
+ pub session_path: String,
+
+ // This is automatically loaded when settings is created. Do not set in
+ // config! Keep secrets and settings apart.
+ pub session_token: String,
+}
+
+impl Settings {
+ pub fn save_sync_time() -> Result<()> {
+ let sync_time_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
+ .ok_or_else(|| eyre!("could not determine key file location"))?;
+ let sync_time_path = sync_time_path.data_dir().join("last_sync_time");
+
+ std::fs::write(sync_time_path, Utc::now().to_rfc3339())?;
+
+ Ok(())
+ }
+
+ pub fn last_sync() -> Result<chrono::DateTime<Utc>> {
+ let sync_time_path = ProjectDirs::from("com", "elliehuxtable", "atuin");
+
+ if sync_time_path.is_none() {
+ debug!("failed to load projectdirs, not syncing");
+ return Err(eyre!("could not load project dirs"));
+ }
+
+ let sync_time_path = sync_time_path.unwrap();
+ let sync_time_path = sync_time_path.data_dir().join("last_sync_time");
+
+ if !sync_time_path.exists() {
+ return Ok(Utc.ymd(1970, 1, 1).and_hms(0, 0, 0));
+ }
+
+ let time = std::fs::read_to_string(sync_time_path)?;
+ let time = chrono::DateTime::parse_from_rfc3339(time.as_str())?;
+
+ Ok(time.with_timezone(&Utc))
+ }
+
+ pub fn should_sync(&self) -> Result<bool> {
+ if !self.auto_sync {
+ return Ok(false);
+ }
+
+ match parse(self.sync_frequency.as_str()) {
+ Ok(d) => {
+ let d = chrono::Duration::from_std(d).unwrap();
+ Ok(Utc::now() - Settings::last_sync()? >= d)
+ }
+ Err(e) => Err(eyre!("failed to check sync: {}", e)),
+ }
+ }
+
+ pub fn new() -> Result<Self> {
+ let config_dir = ProjectDirs::from("com", "elliehuxtable", "atuin").unwrap();
+ let config_dir = config_dir.config_dir();
+
+ create_dir_all(config_dir)?;
+
+ let config_file = if let Ok(p) = std::env::var("ATUIN_CONFIG") {
+ PathBuf::from(p)
+ } else {
+ let mut config_file = PathBuf::new();
+ config_file.push(config_dir);
+ config_file.push("config.toml");
+ config_file
+ };
+
+ let mut s = Config::new();
+
+ let db_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
+ .ok_or_else(|| eyre!("could not determine db file location"))?
+ .data_dir()
+ .join("history.db");
+
+ let key_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
+ .ok_or_else(|| eyre!("could not determine key file location"))?
+ .data_dir()
+ .join("key");
+
+ let session_path = ProjectDirs::from("com", "elliehuxtable", "atuin")
+ .ok_or_else(|| eyre!("could not determine session file location"))?
+ .data_dir()
+ .join("session");
+
+ s.set_default("db_path", db_path.to_str())?;
+ s.set_default("key_path", key_path.to_str())?;
+ s.set_default("session_path", session_path.to_str())?;
+ s.set_default("dialect", "us")?;
+ s.set_default("auto_sync", true)?;
+ s.set_default("sync_frequency", "5m")?;
+ s.set_default("sync_address", "https://api.atuin.sh")?;
+
+ if config_file.exists() {
+ s.merge(ConfigFile::with_name(config_file.to_str().unwrap()))?;
+ } else {
+ let example_config = include_bytes!("../config.toml");
+ let mut file = File::create(config_file)?;
+ file.write_all(example_config)?;
+ }
+
+ s.merge(Environment::with_prefix("atuin").separator("_"))?;
+
+ // all paths should be expanded
+ let db_path = s.get_str("db_path")?;
+ let db_path = shellexpand::full(db_path.as_str())?;
+ s.set("db_path", db_path.to_string())?;
+
+ let key_path = s.get_str("key_path")?;
+ let key_path = shellexpand::full(key_path.as_str())?;
+ s.set("key_path", key_path.to_string())?;
+
+ let session_path = s.get_str("session_path")?;
+ let session_path = shellexpand::full(session_path.as_str())?;
+ s.set("session_path", session_path.to_string())?;
+
+ // Finally, set the auth token
+ if Path::new(session_path.to_string().as_str()).exists() {
+ let token = std::fs::read_to_string(session_path.to_string())?;
+ s.set("session_token", token.trim())?;
+ } else {
+ s.set("session_token", "not logged in")?;
+ }
+
+ s.try_into()
+ .map_err(|e| eyre!("failed to deserialize: {}", e))
+ }
+}