be able to query entries
This commit is contained in:
parent
47f4086c59
commit
a29c3c40ab
|
@ -3,7 +3,9 @@ use std::io::Write;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
||||||
use crate::{error, database::Database};
|
use crate::error;
|
||||||
|
use crate::database::Database;
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
pub mod r#in;
|
pub mod r#in;
|
||||||
pub mod display;
|
pub mod display;
|
||||||
|
@ -11,5 +13,5 @@ pub mod display;
|
||||||
pub trait Command<'a> {
|
pub trait Command<'a> {
|
||||||
type Args: TryFrom<&'a ArgMatches<'a>>;
|
type Args: TryFrom<&'a ArgMatches<'a>>;
|
||||||
|
|
||||||
fn handle<D: Database, W: Write>(args: Self::Args, db: &mut D, out: &mut W) -> error::Result<()>;
|
fn handle<D: Database, W: Write>(args: Self::Args, db: &mut D, out: &mut W, config: &Config) -> error::Result<()>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,18 +3,33 @@ use std::io::Write;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
||||||
use crate::{error, database::Database};
|
use crate::error;
|
||||||
|
use crate::database::Database;
|
||||||
|
use crate::types::Time;
|
||||||
|
use crate::formatters::Formatter;
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
use super::Command;
|
use super::Command;
|
||||||
|
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
ids: bool,
|
||||||
|
start: Option<Time>,
|
||||||
|
end: Option<Time>,
|
||||||
|
format: Formatter,
|
||||||
|
grep: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a ArgMatches<'a>> for Args {
|
impl<'a> TryFrom<&'a ArgMatches<'a>> for Args {
|
||||||
type Error = error::Error;
|
type Error = error::Error;
|
||||||
|
|
||||||
fn try_from(matches: &'a ArgMatches) -> error::Result<Args> {
|
fn try_from(matches: &'a ArgMatches) -> error::Result<Args> {
|
||||||
unimplemented!()
|
Ok(Args {
|
||||||
|
ids: matches.is_present("ids"),
|
||||||
|
start: matches.value_of("at").map(|s| s.parse()).transpose()?,
|
||||||
|
end: matches.value_of("at").map(|s| s.parse()).transpose()?,
|
||||||
|
format: matches.value_of("format").unwrap().parse()?,
|
||||||
|
grep: matches.value_of("grep").map(|s| s.into()),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,11 +39,13 @@ pub struct DisplayCommand {
|
||||||
impl<'a> Command<'a> for DisplayCommand {
|
impl<'a> Command<'a> for DisplayCommand {
|
||||||
type Args = Args;
|
type Args = Args;
|
||||||
|
|
||||||
fn handle<D, W>(args: Self::Args, db: &mut D, out: &mut W) -> error::Result<()>
|
fn handle<D, W>(args: Self::Args, db: &mut D, out: &mut W, config: &Config) -> error::Result<()>
|
||||||
where
|
where
|
||||||
D: Database,
|
D: Database,
|
||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
unimplemented!()
|
let current_sheet = db.current_sheet()?.unwrap_or("default".into());
|
||||||
|
|
||||||
|
args.format.print_formatted(db.entries_by_sheet(¤t_sheet)?, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ use std::convert::TryFrom;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
|
|
||||||
use crate::database::Database;
|
use crate::database::Database;
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::types::Time;
|
use crate::types::Time;
|
||||||
use crate::editor;
|
use crate::editor;
|
||||||
use crate::commands::Command;
|
use crate::commands::Command;
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
at: Option<Time>,
|
at: Option<Time>,
|
||||||
|
@ -31,7 +31,7 @@ pub struct InCommand {}
|
||||||
impl<'a> Command<'a> for InCommand {
|
impl<'a> Command<'a> for InCommand {
|
||||||
type Args = Args;
|
type Args = Args;
|
||||||
|
|
||||||
fn handle<D, W>(args: Args, db: &mut D, out: &mut W) -> error::Result<()>
|
fn handle<D, W>(args: Args, db: &mut D, out: &mut W, config: &Config) -> error::Result<()>
|
||||||
where
|
where
|
||||||
D: Database,
|
D: Database,
|
||||||
W: Write,
|
W: Write,
|
||||||
|
@ -67,7 +67,7 @@ mod tests {
|
||||||
|
|
||||||
assert!(false, "there are no entries");
|
assert!(false, "there are no entries");
|
||||||
|
|
||||||
InCommand::handle(args, &mut d, &mut out).unwrap();
|
InCommand::handle(args, &mut d, &mut out, &Default::default()).unwrap();
|
||||||
|
|
||||||
assert!(false, "there is one entry");
|
assert!(false, "there is one entry");
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,7 @@ mod tests {
|
||||||
|
|
||||||
assert!(false, "there are no entries");
|
assert!(false, "there are no entries");
|
||||||
|
|
||||||
InCommand::handle(args, &mut d, &mut out).unwrap();
|
InCommand::handle(args, &mut d, &mut out, &Default::default()).unwrap();
|
||||||
|
|
||||||
assert!(false, "there are still no entries");
|
assert!(false, "there are still no entries");
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,3 +128,22 @@ impl Config {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Config {
|
||||||
|
Config {
|
||||||
|
database_file: PathBuf::new(), // "/home/abraham/.timetrap.db"
|
||||||
|
round_in_seconds: 900, // 900
|
||||||
|
append_notes_delimiter: " ".into(), //" "
|
||||||
|
formatter_search_paths: Vec::new(), //- "/home/abraham/.timetrap/formatters"
|
||||||
|
default_formatter: Formatter::Text, //text
|
||||||
|
auto_sheet: "dotfiles".into(), //dotfiles
|
||||||
|
auto_sheet_search_paths: Vec::new(), // - "/home/abraham/.timetrap/auto_sheets"
|
||||||
|
default_command: None,
|
||||||
|
auto_checkout: false, // false
|
||||||
|
require_note: true, // true
|
||||||
|
note_editor: "vim".into(), // nvim
|
||||||
|
week_start: WeekDay::Monday, // Monday
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use rusqlite::{Connection, ToSql};
|
use rusqlite::{Connection, ToSql};
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
|
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::models::{Entry, Meta};
|
use crate::models::{Entry, Meta};
|
||||||
|
@ -15,13 +14,22 @@ pub trait Database {
|
||||||
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Entry>>;
|
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Entry>>;
|
||||||
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Meta>>;
|
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Meta>>;
|
||||||
|
|
||||||
fn entries_by_sheet(&mut self, sheet: String) -> error::Result<Vec<Entry>> {
|
// Entry queries
|
||||||
self.entry_query("some".into(), &[])
|
|
||||||
|
fn entries_by_sheet(&mut self, sheet: &str) -> error::Result<Vec<Entry>> {
|
||||||
|
self.entry_query("select * from entries where sheet=?1", &[&sheet])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn entry_insert(&mut self, at: Time, note: String) -> error::Result<()> {
|
fn entry_insert(&mut self, at: Time, note: String) -> error::Result<()> {
|
||||||
self.execute("", &[])
|
self.execute("", &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Meta queries
|
||||||
|
fn current_sheet(&self) -> error::Result<Option<String>> {
|
||||||
|
let results = self.meta_query("select * from meta where key=?1", &[&"current_sheet"])?;
|
||||||
|
|
||||||
|
Ok(results.into_iter().next().map(|m| m.value))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SqliteDatabase {
|
pub struct SqliteDatabase {
|
||||||
|
@ -52,7 +60,7 @@ impl Database for SqliteDatabase {
|
||||||
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Entry>> {
|
fn entry_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Entry>> {
|
||||||
let mut stmt = self.connection.prepare(query)?;
|
let mut stmt = self.connection.prepare(query)?;
|
||||||
|
|
||||||
let results = stmt.query_map([], |row| {
|
let results = stmt.query_map(params, |row| {
|
||||||
let x = Ok(Entry {
|
let x = Ok(Entry {
|
||||||
id: row.get("id")?,
|
id: row.get("id")?,
|
||||||
note: row.get("note")?,
|
note: row.get("note")?,
|
||||||
|
@ -69,7 +77,7 @@ impl Database for SqliteDatabase {
|
||||||
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Meta>> {
|
fn meta_query(&self, query: &str, params: &[&dyn ToSql]) -> error::Result<Vec<Meta>> {
|
||||||
let mut stmt = self.connection.prepare(query)?;
|
let mut stmt = self.connection.prepare(query)?;
|
||||||
|
|
||||||
let results = stmt.query_map([], |row| Ok(Meta {
|
let results = stmt.query_map(params, |row| Ok(Meta {
|
||||||
id: row.get("id")?,
|
id: row.get("id")?,
|
||||||
key: row.get("key")?,
|
key: row.get("key")?,
|
||||||
value: row.get("value")?,
|
value: row.get("value")?,
|
||||||
|
|
|
@ -1,7 +1,43 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use crate::error;
|
||||||
|
use crate::models::Entry;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum Formatter {
|
pub enum Formatter {
|
||||||
Text,
|
Text,
|
||||||
|
Custom(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Formatter {
|
||||||
|
pub fn print_formatted<W: Write>(&self, entries: Vec<Entry>, out: &mut W) -> error::Result<()> {
|
||||||
|
match &self {
|
||||||
|
Formatter::Text => {
|
||||||
|
for entry in entries {
|
||||||
|
writeln!(out, "{:?}", entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Formatter::Custom(name) => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Formatter {
|
||||||
|
type Err = error::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> error::Result<Formatter> {
|
||||||
|
let lower = s.to_lowercase();
|
||||||
|
|
||||||
|
Ok(match &*lower {
|
||||||
|
"text" => Formatter::Text,
|
||||||
|
custom_format => Formatter::Custom(custom_format.into()),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -12,12 +12,12 @@ use tiempo::commands::{ Command, r#in::InCommand, display::DisplayCommand, };
|
||||||
fn error_trap(matches: ArgMatches) -> error::Result<()> {
|
fn error_trap(matches: ArgMatches) -> error::Result<()> {
|
||||||
let config = Config::read()?;
|
let config = Config::read()?;
|
||||||
|
|
||||||
let mut conn = SqliteDatabase::from_path(config.database_file)?;
|
let mut conn = SqliteDatabase::from_path(&config.database_file)?;
|
||||||
let mut out = io::stdout();
|
let mut out = io::stdout();
|
||||||
|
|
||||||
match matches.subcommand() {
|
match matches.subcommand() {
|
||||||
("in", Some(matches)) => InCommand::handle(matches.try_into()?, &mut conn, &mut out),
|
("in", Some(matches)) => InCommand::handle(matches.try_into()?, &mut conn, &mut out, &config),
|
||||||
("display", Some(matches)) => DisplayCommand::handle(matches.try_into()?, &mut conn, &mut out),
|
("display", Some(matches)) => DisplayCommand::handle(matches.try_into()?, &mut conn, &mut out, &config),
|
||||||
(cmd, _) => Err(error::Error::UnimplementedCommand(cmd.into())),
|
(cmd, _) => Err(error::Error::UnimplementedCommand(cmd.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +109,26 @@ fn main() {
|
||||||
.about("Display the current timesheet or a specific. Pass `all' as SHEET
|
.about("Display the current timesheet or a specific. Pass `all' as SHEET
|
||||||
to display all unarchived sheets or `full' to display archived and
|
to display all unarchived sheets or `full' to display archived and
|
||||||
unarchived sheets.")
|
unarchived sheets.")
|
||||||
|
.arg(Arg::with_name("ids")
|
||||||
|
.short("v").long("ids")
|
||||||
|
.help("Print database ids (for use with edit)"))
|
||||||
|
.arg(Arg::with_name("start")
|
||||||
|
.short("s").long("start")
|
||||||
|
.takes_value(true)
|
||||||
|
.value_name("DATE")
|
||||||
|
.help("Include entries that start on this date or later"))
|
||||||
|
.arg(Arg::with_name("end")
|
||||||
|
.short("e").long("end")
|
||||||
|
.takes_value(true).value_name("DATE")
|
||||||
|
.help("Include entries that start on this date or earlier"))
|
||||||
|
.arg(Arg::with_name("format")
|
||||||
|
.short("f").long("format")
|
||||||
|
.takes_value(true).value_name("FORMAT").default_value("text")
|
||||||
|
.help("The output format. Valid built-in formats are ical, csv, json, ids, factor, and text (default). Documentation on defining custom formats can be found in the README included in this distribution."))
|
||||||
|
.arg(Arg::with_name("grep")
|
||||||
|
.short("g").long("grep")
|
||||||
|
.takes_value(true).value_name("REGEXP")
|
||||||
|
.help("Include entries where the note matches this regexp."))
|
||||||
)
|
)
|
||||||
|
|
||||||
.subcommand(SubCommand::with_name("in")
|
.subcommand(SubCommand::with_name("in")
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Entry {
|
pub struct Entry {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub note: String,
|
pub note: String,
|
||||||
|
@ -8,6 +9,7 @@ pub struct Entry {
|
||||||
pub sheet: String,
|
pub sheet: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Meta {
|
pub struct Meta {
|
||||||
pub id: u64,
|
pub id: u64,
|
||||||
pub key: String,
|
pub key: String,
|
||||||
|
|
Loading…
Reference in New Issue