summaryrefslogtreecommitdiffstats
path: root/imag-counter
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2016-02-28 12:26:16 +0100
committerMatthias Beyer <mail@beyermatthias.de>2016-03-16 16:48:24 +0100
commitb079f67918edcdb92888e8b01c273c956397cc34 (patch)
treeb1aa660afa56758980a13450006c4b486fe91cd2 /imag-counter
parent8c220af300540e30fd274d765cfb5c5aacf9c0ee (diff)
Add initial codebase for interactive counting
Diffstat (limited to 'imag-counter')
-rw-r--r--imag-counter/src/interactive.rs154
-rw-r--r--imag-counter/src/main.rs11
-rw-r--r--imag-counter/src/ui.rs14
3 files changed, 173 insertions, 6 deletions
diff --git a/imag-counter/src/interactive.rs b/imag-counter/src/interactive.rs
new file mode 100644
index 00000000..fc7116c8
--- /dev/null
+++ b/imag-counter/src/interactive.rs
@@ -0,0 +1,154 @@
+use std::collections::BTreeMap;
+use std::fmt::{Display, Formatter, Error};
+use std::io::Write;
+use std::io::stderr;
+use std::io::stdin;
+use std::process::exit;
+use std::result::Result as RResult;
+
+use libimagcounter::counter::Counter;
+use libimagcounter::error::CounterError;
+use libimagrt::runtime::Runtime;
+use libimagutil::key_value_split::IntoKeyValue;
+use libimagutil::key_value_split::KeyValue;
+use libimagutil::trace::trace_error;
+
+type Result<T> = RResult<T, CounterError>;
+
+pub fn interactive(rt: &Runtime) {
+ let scmd = rt.cli().subcommand_matches("interactive");
+ if scmd.is_none() {
+ write!(stderr(), "No subcommand");
+ exit(1);
+ }
+ let scmd = scmd.unwrap();
+ debug!("Found 'interactive' command");
+
+ let mut pairs : BTreeMap<char, Binding> = BTreeMap::new();
+
+ for spec in scmd.values_of("spec").unwrap() {
+ match compute_pair(rt, &spec) {
+ Ok((k, v)) => { pairs.insert(k, v); },
+ Err(e) => { trace_error(&e); },
+ }
+ }
+
+ if !has_quit_binding(&pairs) {
+ pairs.insert('q', Binding::Function(String::from("quit"), Box::new(quit)));
+ }
+
+ stderr().flush();
+ loop {
+ println!("---");
+ for (k, v) in &pairs {
+ println!("\t[{}] => {}", k, v);
+ }
+ println!("---");
+ print!("counter > ");
+
+ let mut input = String::new();
+ if let Err(e) = stdin().read_line(&mut input) {
+ trace_error(&e);
+ exit(1);
+ }
+
+ let cont = if input.len() > 0 {
+ let increment = match input.chars().next() { Some('-') => false, _ => true };
+ input.chars().all(|chr| {
+ match pairs.get_mut(&chr) {
+ Some(&mut Binding::Counter(ref mut ctr)) => {
+ if increment {
+ debug!("Incrementing");
+ ctr.inc();
+ } else {
+ debug!("Decrementing");
+ ctr.dec();
+ }
+ true
+ },
+ Some(&mut Binding::Function(ref name, ref f)) => {
+ debug!("Calling {}", name);
+ f()
+ },
+ None => true,
+ }
+ })
+ } else {
+ println!("No input...");
+ println!("\tUse a single character to increment the counter which is bound to it");
+ println!("\tUse 'q' (or the character bound to quit()) to exit");
+ println!("\tPrefix the line with '-' to decrement instead of increment the counters");
+ println!("");
+ true
+ };
+
+ if !cont {
+ break;
+ }
+ }
+}
+
+fn has_quit_binding(pairs: &BTreeMap<char, Binding>) -> bool {
+ pairs.iter()
+ .any(|(_, bind)| {
+ match bind {
+ &Binding::Function(ref name, _) => name == "quit",
+ _ => false,
+ }
+ })
+}
+
+enum Binding<'a> {
+ Counter(Counter<'a>),
+ Function(String, Box<Fn() -> bool>),
+}
+
+impl<'a> Display for Binding<'a> {
+
+ fn fmt(&self, fmt: &mut Formatter) -> RResult<(), Error> {
+ match self {
+ &Binding::Counter(ref c) => {
+ match c.name() {
+ Ok(name) => {
+ try!(write!(fmt, "{}", name));
+ Ok(())
+ },
+ Err(e) => {
+ trace_error(&e);
+ Ok(()) // TODO: Find a better way to escalate here.
+ },
+ }
+ },
+ &Binding::Function(ref name, _) => write!(fmt, "{}()", name),
+ }
+ }
+
+}
+
+fn compute_pair<'a>(rt: &'a Runtime, spec: &str) -> Result<(char, Binding<'a>)> {
+ let kv = String::from(spec).into_kv();
+ if kv.is_none() {
+ write!(stderr(), "Key-Value parsing failed!");
+ exit(1);
+ }
+ let kv = kv.unwrap();
+
+ let (k, v) = kv.into();
+ if !k.len() == 1 {
+ // We have a key which is not only a single character!
+ exit(1);
+ }
+
+ if v == "quit" {
+ // TODO uncaught unwrap()
+ Ok((k.chars().next().unwrap(), Binding::Function(String::from("quit"), Box::new(quit))))
+ } else {
+ // TODO uncaught unwrap()
+ Counter::load(v, rt.store()).and_then(|ctr| Ok((k.chars().next().unwrap(), Binding::Counter(ctr))))
+ }
+}
+
+fn quit() -> bool {
+ false
+}
+
diff --git a/imag-counter/src/main.rs b/imag-counter/src/main.rs
index cfc93142..961db48c 100644
--- a/imag-counter/src/main.rs
+++ b/imag-counter/src/main.rs
@@ -16,11 +16,13 @@ use libimagutil::key_value_split::IntoKeyValue;
mod create;
mod delete;
+mod interactive;
mod ui;
use ui::build_ui;
use create::create;
use delete::delete;
+use interactive::interactive;
enum Action {
Inc,
@@ -121,14 +123,13 @@ fn main() {
|name| {
debug!("Call: {}", name);
match name {
- "create" => create(&rt),
- "delete" => delete(&rt),
+ "create" => create(&rt),
+ "delete" => delete(&rt),
+ "interactive" => interactive(&rt),
_ => {
debug!("Unknown command"); // More error handling
},
};
})
-
-
-
}
+
diff --git a/imag-counter/src/ui.rs b/imag-counter/src/ui.rs
index 610a4241..994bb65e 100644
--- a/imag-counter/src/ui.rs
+++ b/imag-counter/src/ui.rs
@@ -53,6 +53,18 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
.takes_value(true)
.required(true)
.help("Create counter with this name")))
-}
+ .subcommand(SubCommand::with_name("interactive")
+ .about("Interactively count things")
+ .version("0.1")
+ .arg(Arg::with_name("spec")
+ .long("spec")
+ .short("s")
+ .takes_value(true)
+ .multiple(true)
+ .required(true)
+ .help("Specification for key-bindings. Use <KEY>=<VALUE> where KEY is the
+ key to bind (single character) and VALUE is the path to the counter to bind
+ to.")))
+}