a good start for this project
This commit is contained in:
commit
1655803076
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,450 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dtoa"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.22.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.25.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57adcf67c8faaf96f3248c2a7b419a0dbc52ebe36ba83dd57fe83827c1ea4eb3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"memchr",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.8.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23"
|
||||
dependencies = [
|
||||
"dtoa",
|
||||
"linked-hash-map",
|
||||
"serde",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiempo"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"dirs",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_yaml",
|
||||
"thiserror",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "025ce40a007e1907e58d5bc1a594def78e5573bb0b1160bc389634e8f12e4faa"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
|
||||
dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "tiempo"
|
||||
version = "0.1.0"
|
||||
authors = ["Abraham Toriz <categulario@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = "2"
|
||||
chrono = "0.4"
|
||||
rusqlite = "0.25.3"
|
||||
thiserror = "*"
|
||||
dirs = "*"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_yaml = "0.8"
|
||||
toml = "0.5"
|
|
@ -0,0 +1,15 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::io::Write;
|
||||
|
||||
use clap::ArgMatches;
|
||||
|
||||
use crate::{error, database::Database};
|
||||
|
||||
pub mod r#in;
|
||||
pub mod display;
|
||||
|
||||
pub trait Command<'a> {
|
||||
type Args: TryFrom<&'a ArgMatches<'a>>;
|
||||
|
||||
fn handle<D: Database, W: Write>(args: Self::Args, db: &mut D, out: &mut W) -> error::Result<()>;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::io::Write;
|
||||
|
||||
use clap::ArgMatches;
|
||||
|
||||
use crate::{error, database::Database};
|
||||
|
||||
use super::Command;
|
||||
|
||||
pub struct Args {
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a ArgMatches<'a>> for Args {
|
||||
type Error = error::Error;
|
||||
|
||||
fn try_from(matches: &'a ArgMatches) -> error::Result<Args> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DisplayCommand {
|
||||
}
|
||||
|
||||
impl<'a> Command<'a> for DisplayCommand {
|
||||
type Args = Args;
|
||||
|
||||
fn handle<D, W>(args: Self::Args, db: &mut D, out: &mut W) -> error::Result<()>
|
||||
where
|
||||
D: Database,
|
||||
W: Write,
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::io::Write;
|
||||
|
||||
use clap::ArgMatches;
|
||||
|
||||
use crate::{database::Database, error, types::Time, editor, commands::Command};
|
||||
|
||||
pub struct Args {
|
||||
at: Option<Time>,
|
||||
note: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a ArgMatches<'a>> for Args {
|
||||
type Error = error::Error;
|
||||
|
||||
fn try_from(matches: &'a ArgMatches) -> Result<Self, Self::Error> {
|
||||
Ok(Args {
|
||||
at: matches.value_of("at").map(|s| s.parse()).transpose()?,
|
||||
note: matches.value_of("note").map(|s| s.into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InCommand {}
|
||||
|
||||
impl<'a> Command<'a> for InCommand {
|
||||
type Args = Args;
|
||||
|
||||
fn handle<D, W>(args: Args, db: &mut D, out: &mut W) -> error::Result<()>
|
||||
where
|
||||
D: Database,
|
||||
W: Write,
|
||||
{
|
||||
let at = args.at.unwrap_or(Time::now());
|
||||
|
||||
let note = if let Some(note) = args.note {
|
||||
note
|
||||
} else {
|
||||
editor::get_string()?
|
||||
};
|
||||
|
||||
db.entry_insert(at, note)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::database::SqliteDatabase;
|
||||
|
||||
#[test]
|
||||
fn test_handles_new_entry() {
|
||||
let mut d = SqliteDatabase::from_memory().unwrap();
|
||||
let args = Args {
|
||||
at: None,
|
||||
note: Some("hola".into()),
|
||||
};
|
||||
let out = Vec::new();
|
||||
|
||||
assert!(false, "there are no entries");
|
||||
|
||||
InCommand::handle(args, &mut d, &mut out).unwrap();
|
||||
|
||||
assert!(false, "there is one entry");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_handles_already_running_entry() {
|
||||
let mut d = SqliteDatabase::from_memory().unwrap();
|
||||
let args = Args {
|
||||
at: None,
|
||||
note: Some("hola".into()),
|
||||
};
|
||||
let mut out = Vec::new();
|
||||
|
||||
assert!(false, "there are no entries");
|
||||
|
||||
InCommand::handle(args, &mut d, &mut out).unwrap();
|
||||
|
||||
assert!(false, "there are still no entries");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_with_no_time_given_uses_now() {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sets_given_time() {
|
||||
assert!(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use dirs::home_dir;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use crate::{error::{self, Error::*}, formatters::Formatter};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum WeekDay {
|
||||
Monday,
|
||||
Tuesday,
|
||||
Wednesday,
|
||||
Thursday,
|
||||
Friday,
|
||||
Saturday,
|
||||
Sunday,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
/// Absolute path to the sqlite database
|
||||
pub database_file: PathBuf, // "/home/abraham/.timetrap.db"
|
||||
|
||||
/// The duration of time to use for rounding with the -r flag
|
||||
pub round_in_seconds: u32, // 900
|
||||
|
||||
/// delimiter used when appending notes via t edit --append
|
||||
pub append_notes_delimiter: String, //" "
|
||||
|
||||
/// an array of directories to search for user defined fomatter classes
|
||||
pub formatter_search_paths: Vec<PathBuf>, //- "/home/abraham/.timetrap/formatters"
|
||||
|
||||
/// The format to use when display is invoked without a --format option
|
||||
pub default_formatter: Formatter, //text
|
||||
|
||||
/// Which auto sheet module to use.
|
||||
pub auto_sheet: String, //dotfiles
|
||||
|
||||
/// an array of directories to search for user defined auto_sheet classes
|
||||
pub auto_sheet_search_paths: Vec<PathBuf>, // - "/home/abraham/.timetrap/auto_sheets"
|
||||
|
||||
/// The default command to invoke when you call t
|
||||
pub default_command: Option<String>,
|
||||
|
||||
/// Automatically check out of running entries when you check in or out
|
||||
pub auto_checkout: bool, // false
|
||||
|
||||
/// Prompt for a note if one isn't provided when checking in
|
||||
pub require_note: bool, // true
|
||||
|
||||
/// The command to start editing notes. Defaults to false which means no
|
||||
/// external editor is used. Please see the section below on Notes Editing
|
||||
/// for tips on using non-terminal based editors. Example: note_editor:
|
||||
/// "vim"
|
||||
pub note_editor: String, // nvim
|
||||
|
||||
/// The day of the week to use as the start of the week for t week.
|
||||
pub week_start: WeekDay, // Monday
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Tries as hard as possible to read the current configuration. Retrieving
|
||||
/// the path to it from the environment or common locations.
|
||||
pub fn read() -> error::Result<Config> {
|
||||
// first try from env variable TIMETRAP_CONFIG_FILE
|
||||
if let Ok(value) = env::var("TIMETRAP_CONFIG_FILE") {
|
||||
if value.ends_with(".toml") {
|
||||
return Ok(Self::read_from_toml(value)?);
|
||||
} else {
|
||||
return Ok(Self::read_from_yaml(value)?);
|
||||
}
|
||||
}
|
||||
|
||||
// Next try from some known directories
|
||||
if let Some(home) = home_dir() {
|
||||
let mut old_location = home.clone();
|
||||
old_location.push(".timetrap.yml");
|
||||
|
||||
if old_location.is_file() {
|
||||
return Self::read_from_yaml(old_location);
|
||||
}
|
||||
|
||||
let mut new_location = home.clone();
|
||||
new_location.push(".config");
|
||||
new_location.push("timetrap");
|
||||
new_location.push("config.toml");
|
||||
|
||||
if new_location.is_file() {
|
||||
Self::read_from_toml(new_location)
|
||||
} else {
|
||||
Self::create_and_return_config()
|
||||
}
|
||||
} else {
|
||||
Err(error::Error::NoHomeDir)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_from_yaml<P: AsRef<Path>>(path: P) -> error::Result<Config> {
|
||||
let path: PathBuf = path.as_ref().into();
|
||||
|
||||
let mut contents = String::new();
|
||||
let mut file = File::open(&path).map_err(|_| FileNotFound(path.clone()))?;
|
||||
|
||||
file.read_to_string(&mut contents).map_err(|_| CouldntRead(path.clone()))?;
|
||||
|
||||
serde_yaml::from_str(&contents).map_err(|error| YamlError {
|
||||
path, error
|
||||
})
|
||||
}
|
||||
|
||||
fn read_from_toml<P: AsRef<Path>>(path: P) -> error::Result<Config> {
|
||||
let path: PathBuf = path.as_ref().into();
|
||||
|
||||
let mut contents = String::new();
|
||||
let mut file = File::open(&path).map_err(|_| FileNotFound(path.clone()))?;
|
||||
|
||||
file.read_to_string(&mut contents).map_err(|_| CouldntRead(path.clone()))?;
|
||||
|
||||
toml::from_str(&contents).map_err(|error| TomlError {
|
||||
path, error
|
||||
})
|
||||
}
|
||||
|
||||
fn create_and_return_config() -> error::Result<Config> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
use rusqlite::Connection;
|
||||
|
||||
use crate::{types::Time, error};
|
||||
|
||||
pub trait Database {
|
||||
fn entry_insert(&mut self, at: Time, note: String) -> error::Result<()>;
|
||||
}
|
||||
|
||||
pub struct SqliteDatabase {
|
||||
}
|
||||
|
||||
impl SqliteDatabase {
|
||||
pub fn from_memory() -> error::Result<impl Database> {
|
||||
Ok(SqliteDatabase {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_path(path: &str) -> error::Result<impl Database> {
|
||||
let conn = Connection::open(path)?;
|
||||
|
||||
Ok(SqliteDatabase {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for SqliteDatabase {
|
||||
fn entry_insert(&mut self, at: Time, note: String) -> error::Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
use crate::error;
|
||||
|
||||
pub fn get_string() -> error::Result<String> {
|
||||
unimplemented!()
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
use std::result;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("The command '{0}' is not implemented")]
|
||||
UnimplementedCommand(String),
|
||||
|
||||
#[error("Sqlite error: {0}")]
|
||||
Sqlite(#[from] rusqlite::Error),
|
||||
|
||||
#[error("Could not find home directory :(")]
|
||||
NoHomeDir,
|
||||
|
||||
#[error("The file {0} was not found")]
|
||||
FileNotFound(PathBuf),
|
||||
|
||||
#[error("Error reading the file: {0}")]
|
||||
CouldntRead(PathBuf),
|
||||
|
||||
#[error("Couldn't parse toml file at: {path}\nwith error: {error}")]
|
||||
TomlError{ path: PathBuf, error: toml::de::Error},
|
||||
|
||||
#[error("Couldn't parse yaml file at: {path}\nwith error: {error}")]
|
||||
YamlError{ path: PathBuf, error: serde_yaml::Error},
|
||||
}
|
||||
|
||||
pub type Result<T> = result::Result<T, Error>;
|
|
@ -0,0 +1,7 @@
|
|||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Formatter {
|
||||
Text,
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
pub mod types;
|
||||
pub mod commands;
|
||||
pub mod database;
|
||||
pub mod config;
|
||||
pub mod editor;
|
||||
pub mod formatters;
|
||||
pub mod error;
|
|
@ -0,0 +1,146 @@
|
|||
use std::convert::TryInto;
|
||||
use std::process::exit;
|
||||
use std::io;
|
||||
|
||||
use clap::{App, Arg, SubCommand, AppSettings, crate_version, ArgMatches};
|
||||
|
||||
use tiempo::error;
|
||||
use tiempo::database::SqliteDatabase;
|
||||
use tiempo::config::Config;
|
||||
use tiempo::commands::{ Command, r#in::InCommand, display::DisplayCommand, };
|
||||
|
||||
fn error_trap(matches: ArgMatches) -> error::Result<()> {
|
||||
let config = Config::read()?;
|
||||
|
||||
dbg!(config);
|
||||
|
||||
let mut conn = SqliteDatabase::from_path("db.sqlite3")?;
|
||||
let mut out = io::stdout();
|
||||
|
||||
match matches.subcommand() {
|
||||
("in", Some(matches)) => InCommand::handle(matches.try_into()?, &mut conn, &mut out),
|
||||
("display", Some(matches)) => DisplayCommand::handle(matches.try_into()?, &mut conn, &mut out),
|
||||
(cmd, _) => Err(error::Error::UnimplementedCommand(cmd.into())),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let matches = App::new("Tiempo")
|
||||
.name("t")
|
||||
.setting(AppSettings::SubcommandRequired)
|
||||
.version(crate_version!())
|
||||
.author("Abraham Toriz <categulario@gmail.com>")
|
||||
.about("Tiempo helps you keep track of time spent in different activities")
|
||||
|
||||
.subcommand(SubCommand::with_name("archive")
|
||||
.visible_alias("a")
|
||||
.about("Move entries to a hidden sheet (by default named '_[SHEET]') so they're out of the way.")
|
||||
.arg(Arg::with_name("start")
|
||||
.long("start")
|
||||
.short("s")
|
||||
.takes_value(true)
|
||||
.value_name("TIME")
|
||||
.help("Include entries that start on this date or later"))
|
||||
.arg(Arg::with_name("end")
|
||||
.long("end")
|
||||
.short("e")
|
||||
.takes_value(true)
|
||||
.value_name("TIME")
|
||||
.help("Include entries that start on this date or earlier"))
|
||||
.arg(Arg::with_name("grep")
|
||||
.long("grep")
|
||||
.short("g")
|
||||
.takes_value(true)
|
||||
.value_name("REGEXP")
|
||||
.help("Include entries where the note matches this regexp."))
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("backend")
|
||||
.visible_alias("b")
|
||||
.about("Open an sqlite shell to the database.")
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("configure")
|
||||
.visible_alias("c")
|
||||
.about("Configure tiempo. Print path to config file.")
|
||||
.arg(Arg::with_name("round_in_seconds")
|
||||
.long("round-in-seconds")
|
||||
.takes_value(true)
|
||||
.value_name("SECONDS")
|
||||
.help("The duration of time to use for rounding with the -r flag"))
|
||||
.arg(Arg::with_name("database_file")
|
||||
.long("database-file")
|
||||
.takes_value(true)
|
||||
.value_name("PATH")
|
||||
.help("The file path of the sqlite database"))
|
||||
.arg(Arg::with_name("append_notes_delimiter")
|
||||
.long("append-notes-delimiter")
|
||||
.takes_value(true)
|
||||
.value_name("DELIMITER")
|
||||
.help("delimiter used when appending notes via t edit --append"))
|
||||
.arg(Arg::with_name("formatter_search_paths")
|
||||
.long("formatter-search-paths")
|
||||
.takes_value(true)
|
||||
.value_name("PATHS")
|
||||
.help("comma separated directories to search for user defined fomatter classes"))
|
||||
.arg(Arg::with_name("default_formatter")
|
||||
.long("default-formatter")
|
||||
.takes_value(true)
|
||||
.value_name("FORMATTER")
|
||||
.help("The format to use when display is invoked without a `--format` option"))
|
||||
.arg(Arg::with_name("require_note")
|
||||
.long("require-note")
|
||||
.help("Prompt for a note if one isn't provided when checking in"))
|
||||
.arg(Arg::with_name("no_require_note")
|
||||
.long("no-require-note")
|
||||
.help("Prompt for a note if one isn't provided when checking in"))
|
||||
.arg(Arg::with_name("note_editor")
|
||||
.long("note-editor")
|
||||
.takes_value(true)
|
||||
.value_name("EDITOR")
|
||||
.help("Command to launch notes editor or false if no editor use."))
|
||||
.arg(Arg::with_name("week_start")
|
||||
.long("week-start")
|
||||
.takes_value(true)
|
||||
.value_name("DAY")
|
||||
.help("The day of the week to use as the start of the week for t week."))
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("display")
|
||||
.visible_alias("d")
|
||||
.about("Display the current timesheet or a specific. Pass `all' as SHEET
|
||||
to display all unarchived sheets or `full' to display archived and
|
||||
unarchived sheets.")
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("in")
|
||||
.visible_alias("i")
|
||||
.about("Start an activity in the current timesheet")
|
||||
.arg(Arg::with_name("at")
|
||||
.long("at")
|
||||
.takes_value(true)
|
||||
.value_name("TIME")
|
||||
.help("Use this time instead of now"))
|
||||
.arg(Arg::with_name("note")
|
||||
.takes_value(true)
|
||||
.value_name("NOTE")
|
||||
.help("Text describing the activity to start"))
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("out")
|
||||
.visible_alias("o")
|
||||
.about("end the active entry in the current timesheet")
|
||||
)
|
||||
|
||||
.subcommand(SubCommand::with_name("sheet")
|
||||
.visible_alias("s")
|
||||
.about("Change active timesheet or list existing timesheets")
|
||||
)
|
||||
|
||||
.get_matches();
|
||||
|
||||
if let Err(e) = error_trap(matches) {
|
||||
eprintln!("{}", e);
|
||||
exit(1);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use crate::error;
|
||||
|
||||
pub struct Time;
|
||||
|
||||
impl Time {
|
||||
pub fn now() -> Time {
|
||||
Time
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Time {
|
||||
type Err = error::Error;
|
||||
|
||||
fn from_str(s: &str) -> error::Result<Time> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue