Merge pull request #2 from conradludgate/main

feat: use directories project data dir
This commit is contained in:
Ellie Huxtable 2020-10-05 19:25:34 +01:00 committed by GitHub
commit 46285309fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 55 deletions

10
Cargo.lock generated
View file

@ -121,6 +121,15 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "directories"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fed639d60b58d0f53498ab13d26f621fd77569cc6edb031f4cc36a2ad9da0f"
dependencies = [
"dirs-sys",
]
[[package]] [[package]]
name = "dirs" name = "dirs"
version = "2.0.2" version = "2.0.2"
@ -438,6 +447,7 @@ name = "shync"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"chrono", "chrono",
"directories",
"eyre", "eyre",
"log", "log",
"pretty_env_logger", "pretty_env_logger",

View file

@ -14,3 +14,4 @@ eyre = "0.6.1"
shellexpand = "2.0.0" shellexpand = "2.0.0"
rusqlite = "0.24.0" rusqlite = "0.24.0"
structopt = "0.3.15" structopt = "0.3.15"
directories = "3.0.1"

View file

@ -1,12 +1,11 @@
use std::path::Path; use std::path::Path;
use eyre::Result; use eyre::Result;
use shellexpand;
use rusqlite::{params, Connection};
use rusqlite::NO_PARAMS; use rusqlite::NO_PARAMS;
use rusqlite::{params, Connection};
use super::history::History; use crate::History;
pub trait Database { pub trait Database {
fn save(&self, h: History) -> Result<()>; fn save(&self, h: History) -> Result<()>;
@ -19,23 +18,25 @@ pub struct SqliteDatabase {
conn: Connection, conn: Connection,
} }
impl SqliteDatabase{ impl SqliteDatabase {
pub fn new(path: &str) -> Result<SqliteDatabase> { pub fn new(path: impl AsRef<Path>) -> Result<SqliteDatabase> {
let path = shellexpand::full(path)?;
let path = path.as_ref(); let path = path.as_ref();
debug!("opening sqlite database at {:?}", path); debug!("opening sqlite database at {:?}", path);
let create = !Path::new(path).exists(); let create = !path.exists();
if create {
if let Some(dir) = path.parent() {
std::fs::create_dir_all(dir)?;
}
}
let conn = Connection::open(path)?; let conn = Connection::open(path)?;
if create { if create {
Self::setup_db(&conn)?; Self::setup_db(&conn)?;
} }
Ok(SqliteDatabase{ Ok(SqliteDatabase { conn })
conn: conn,
})
} }
fn setup_db(conn: &Connection) -> Result<()> { fn setup_db(conn: &Connection) -> Result<()> {
@ -43,11 +44,11 @@ impl SqliteDatabase{
conn.execute( conn.execute(
"create table if not exists history ( "create table if not exists history (
id integer primary key, id integer primary key,
timestamp integer not null, timestamp integer not null,
command text not null, command text not null,
cwd text not null cwd text not null
)", )",
NO_PARAMS, NO_PARAMS,
)?; )?;
@ -61,19 +62,22 @@ impl Database for SqliteDatabase {
self.conn.execute( self.conn.execute(
"insert into history ( "insert into history (
timestamp, timestamp,
command, command,
cwd cwd
) values (?1, ?2, ?3)", ) values (?1, ?2, ?3)",
params![h.timestamp, h.command, h.cwd])?; params![h.timestamp, h.command, h.cwd],
)?;
Ok(()) Ok(())
} }
fn list(&self) -> Result<()> { fn list(&self) -> Result<()> {
debug!("listing history"); debug!("listing history");
let mut stmt = self.conn.prepare("SELECT timestamp, command, cwd FROM history")?; let mut stmt = self
.conn
.prepare("SELECT timestamp, command, cwd FROM history")?;
let history_iter = stmt.query_map(params![], |row| { let history_iter = stmt.query_map(params![], |row| {
Ok(History { Ok(History {
timestamp: row.get(0)?, timestamp: row.get(0)?,

View file

@ -8,11 +8,11 @@ pub struct History {
} }
impl History { impl History {
pub fn new(command: &str, cwd: &str) -> History { pub fn new(command: String, cwd: String) -> History {
History { History {
timestamp: chrono::Utc::now().timestamp_millis(), timestamp: chrono::Utc::now().timestamp_millis(),
command: command.to_string(), command,
cwd: cwd.to_string(), cwd,
} }
} }
} }

View file

@ -1,2 +1,2 @@
pub mod history;
pub mod database; pub mod database;
pub mod history;

View file

@ -1,45 +1,72 @@
use std::env; use std::env;
use std::path::PathBuf;
use directories::ProjectDirs;
use eyre::{eyre, Result};
use structopt::StructOpt; use structopt::StructOpt;
use eyre::Result;
#[macro_use] extern crate log; #[macro_use]
extern crate log;
use pretty_env_logger; use pretty_env_logger;
mod local; mod local;
use local::history::History;
use local::database::{Database, SqliteDatabase}; use local::database::{Database, SqliteDatabase};
use local::history::History;
#[derive(StructOpt)] #[derive(StructOpt)]
#[structopt( #[structopt(
author="Ellie Huxtable <e@elm.sh>", author = "Ellie Huxtable <e@elm.sh>",
version="0.1.0", version = "0.1.0",
about="Keep your shell history in sync" about = "Keep your shell history in sync"
)] )]
enum Shync { struct Shync {
#[structopt(long, parse(from_os_str), help = "db file path")]
db: Option<PathBuf>,
#[structopt(subcommand)]
shync: ShyncCmd,
}
#[derive(StructOpt)]
enum ShyncCmd {
#[structopt( #[structopt(
about="manipulate shell history", about="manipulate shell history",
aliases=&["h", "hi", "his", "hist", "histo", "histor"], aliases=&["h", "hi", "his", "hist", "histo", "histor"],
)] )]
History(HistoryCmd), History(HistoryCmd),
#[structopt( #[structopt(about = "import shell history from file")]
about="import shell history from file",
)]
Import, Import,
#[structopt( #[structopt(about = "start a shync server")]
about="start a shync server",
)]
Server, Server,
} }
impl Shync { impl Shync {
fn run(self, db: SqliteDatabase) -> Result<()> { fn run(self) -> Result<()> {
match self { let db_path = match self.db {
Shync::History(history) => history.run(db), Some(db_path) => {
_ => Ok(()) let path = db_path
.to_str()
.ok_or(eyre!("path {:?} was not valid UTF-8", db_path))?;
let path = shellexpand::full(path)?;
PathBuf::from(path.as_ref())
}
None => {
let project_dirs = ProjectDirs::from("bike", "ellie", "shync").ok_or(eyre!(
"could not determine db file location\nspecify one using the --db flag"
))?;
let root = project_dirs.data_dir();
root.join("history.db")
}
};
let db = SqliteDatabase::new(db_path)?;
match self.shync {
ShyncCmd::History(history) => history.run(db),
_ => Ok(()),
} }
} }
} }
@ -50,9 +77,7 @@ enum HistoryCmd {
about="add a new command to the history", about="add a new command to the history",
aliases=&["a", "ad"], aliases=&["a", "ad"],
)] )]
Add { Add { command: Vec<String> },
command: Vec<String>,
},
#[structopt( #[structopt(
about="list all items in history", about="list all items in history",
@ -64,14 +89,10 @@ enum HistoryCmd {
impl HistoryCmd { impl HistoryCmd {
fn run(self, db: SqliteDatabase) -> Result<()> { fn run(self, db: SqliteDatabase) -> Result<()> {
match self { match self {
HistoryCmd::Add{command: words} => { HistoryCmd::Add { command: words } => {
let command = words.join(" "); let command = words.join(" ");
let cwd = env::current_dir()?.display().to_string();
let cwd = env::current_dir()?; let h = History::new(command, cwd);
let h = History::new(
command.as_str(),
cwd.display().to_string().as_str(),
);
debug!("adding history: {:?}", h); debug!("adding history: {:?}", h);
db.save(h)?; db.save(h)?;
@ -79,7 +100,7 @@ impl HistoryCmd {
Ok(()) Ok(())
} }
HistoryCmd::List => db.list() HistoryCmd::List => db.list(),
} }
} }
} }
@ -87,6 +108,5 @@ impl HistoryCmd {
fn main() -> Result<()> { fn main() -> Result<()> {
pretty_env_logger::init(); pretty_env_logger::init();
let db = SqliteDatabase::new("~/.history.db")?; Shync::from_args().run()
Shync::from_args().run(db)
} }