Fix merge

...I forgot to push. oops.
This commit is contained in:
Ellie Huxtable 2020-10-05 20:36:15 +01:00
commit 9917d6c1e2
6 changed files with 247 additions and 91 deletions

120
Cargo.lock generated
View File

@ -127,6 +127,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"
@ -194,6 +203,15 @@ dependencies = [
"wasi 0.9.0+wasi-snapshot-preview1", "wasi 0.9.0+wasi-snapshot-preview1",
] ]
[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
dependencies = [
"unicode-segmentation",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.1.16" version = "0.1.16"
@ -226,9 +244,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.78" version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98" checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
[[package]] [[package]]
name = "libsqlite3-sys" name = "libsqlite3-sys"
@ -312,12 +330,54 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
[[package]] [[package]]
name = "quick-error" name = "quick-error"
version = "1.2.3" version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.1.57" version = "0.1.57"
@ -394,12 +454,13 @@ name = "shync"
version = "0.1.1" version = "0.1.1"
dependencies = [ dependencies = [
"chrono", "chrono",
"clap", "directories",
"eyre", "eyre",
"log", "log",
"pretty_env_logger", "pretty_env_logger",
"rusqlite", "rusqlite",
"shellexpand", "shellexpand",
"structopt",
] ]
[[package]] [[package]]
@ -414,6 +475,41 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "structopt"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33f6461027d7f08a13715659b2948e1602c31a3756aeae9378bfe7518c72e82"
dependencies = [
"clap",
"lazy_static",
"structopt-derive",
]
[[package]]
name = "structopt-derive"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c92e775028122a4b3dd55d58f14fc5120289c69bee99df1d117ae30f84b225c9"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c51d92969d209b54a98397e1b91c8ae82d8c87a7bb87df0b29aa2ad81454228"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.1.0" version = "1.1.0"
@ -452,12 +548,24 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "unicode-segmentation"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.8" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]] [[package]]
name = "vcpkg" name = "vcpkg"
version = "0.2.10" version = "0.2.10"
@ -470,6 +578,12 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.9.0+wasi-snapshot-preview1" version = "0.9.0+wasi-snapshot-preview1"

View File

@ -11,10 +11,11 @@ description = "shync - sync your shell history"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
pretty_env_logger = "0.4" pretty_env_logger = "0.4"
clap = "2.33.3"
chrono = "0.4.19" chrono = "0.4.19"
eyre = "0.6.1" eyre = "0.6.1"
shellexpand = "2.0.0" shellexpand = "2.0.0"
structopt = "0.3.15"
directories = "3.0.1"
[dependencies.rusqlite] [dependencies.rusqlite]
version = "0.24.0" version = "0.24.0"

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,75 +1,112 @@
use std::env; use std::env;
use std::path::PathBuf;
use clap::{Arg, App, SubCommand}; use directories::ProjectDirs;
use eyre::Result; use eyre::{eyre, Result};
use structopt::StructOpt;
#[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)]
#[structopt(
author = "Ellie Huxtable <e@elm.sh>",
version = "0.1.0",
about = "Keep your shell history in sync"
)]
struct Shync {
#[structopt(long, parse(from_os_str), help = "db file path")]
db: Option<PathBuf>,
#[structopt(subcommand)]
shync: ShyncCmd,
}
#[derive(StructOpt)]
enum ShyncCmd {
#[structopt(
about="manipulate shell history",
aliases=&["h", "hi", "his", "hist", "histo", "histor"],
)]
History(HistoryCmd),
#[structopt(about = "import shell history from file")]
Import,
#[structopt(about = "start a shync server")]
Server,
}
impl Shync {
fn run(self) -> Result<()> {
let db_path = match self.db {
Some(db_path) => {
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(()),
}
}
}
#[derive(StructOpt)]
enum HistoryCmd {
#[structopt(
about="add a new command to the history",
aliases=&["a", "ad"],
)]
Add { command: Vec<String> },
#[structopt(
about="list all items in history",
aliases=&["l", "li", "lis"],
)]
List,
}
impl HistoryCmd {
fn run(self, db: SqliteDatabase) -> Result<()> {
match self {
HistoryCmd::Add { command: words } => {
let command = words.join(" ");
let cwd = env::current_dir()?.display().to_string();
let h = History::new(command, cwd);
debug!("adding history: {:?}", h);
db.save(h)?;
debug!("saved history to sqlite");
Ok(())
}
HistoryCmd::List => db.list(),
}
}
}
fn main() -> Result<()> { fn main() -> Result<()> {
pretty_env_logger::init(); pretty_env_logger::init();
let db = SqliteDatabase::new("~/.history.db")?; Shync::from_args().run()
let matches = App::new("Shync")
.version("0.1.0")
.author("Ellie Huxtable <e@elm.sh>")
.about("Keep your shell history in sync")
.subcommand(
SubCommand::with_name("history")
.aliases(&["h", "hi", "his", "hist", "histo", "histor"])
.about("manipulate shell history")
.subcommand(
SubCommand::with_name("add")
.aliases(&["a", "ad"])
.about("add a new command to the history")
.arg(
Arg::with_name("command")
.multiple(true)
.required(true)
)
)
.subcommand(
SubCommand::with_name("list")
.aliases(&["l", "li", "lis"])
.about("list all items in history")
)
)
.subcommand(
SubCommand::with_name("import")
.about("import shell history from file")
)
.subcommand(
SubCommand::with_name("server")
.about("start a shync server")
)
.get_matches();
if let Some(m) = matches.subcommand_matches("history") {
if let Some(m) = m.subcommand_matches("add") {
let words: Vec<&str> = m.values_of("command").unwrap().collect();
let command = words.join(" ");
let cwd = env::current_dir()?;
let h = History::new(
command.as_str(),
cwd.display().to_string().as_str(),
);
debug!("adding history: {:?}", h);
db.save(h)?;
debug!("saved history to sqlite");
}
else if let Some(_m) = m.subcommand_matches("list") {
db.list()?;
}
}
Ok(())
} }