aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/commands/fav.rs14
-rw-r--r--src/commands/thread.rs20
-rw-r--r--src/commands/twete.rs12
-rw-r--r--src/commands/view.rs33
-rw-r--r--src/display/mod.rs82
-rw-r--r--src/main.rs43
-rw-r--r--src/tw/mod.rs185
-rw-r--r--src/tw/user.rs2
8 files changed, 233 insertions, 158 deletions
diff --git a/src/commands/fav.rs b/src/commands/fav.rs
index 7b9cce6..1cb41e4 100644
--- a/src/commands/fav.rs
+++ b/src/commands/fav.rs
@@ -5,8 +5,6 @@ use tw::TweetId;
use commands::Command;
-use std::str::FromStr;
-
static FAV_TWEET_URL: &str = "https://api.twitter.com/1.1/favorites/create.json";
static UNFAV_TWEET_URL: &str = "https://api.twitter.com/1.1/favorites/destroy.json";
@@ -21,12 +19,12 @@ fn unfav(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
// let inner_twid = u64::from_str(&line).unwrap();
let maybe_id = TweetId::parse(line.to_owned());
match maybe_id {
- Some(twid) => {
+ Ok(twid) => {
let twete = tweeter.retrieve_tweet(&twid).unwrap();
queryer.do_api_post(&format!("{}?id={}", UNFAV_TWEET_URL, twete.id));
}
- None => {
- println!("Invalid id: {}", line);
+ Err(e) => {
+ println!("Invalid id: {}", e);
}
}
}
@@ -41,12 +39,12 @@ fn fav(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
// TODO handle this unwrap
let maybe_id = TweetId::parse(line.to_owned());
match maybe_id {
- Some(twid) => {
+ Ok(twid) => {
let twete = tweeter.retrieve_tweet(&twid).unwrap();
queryer.do_api_post(&format!("{}?id={}", FAV_TWEET_URL, twete.id));
}
- None => {
- println!("Invalid id: {}", line);
+ Err(e) => {
+ println!("Invalid id: {}", e);
}
}
}
diff --git a/src/commands/thread.rs b/src/commands/thread.rs
index 57b410c..fd491ba 100644
--- a/src/commands/thread.rs
+++ b/src/commands/thread.rs
@@ -6,8 +6,6 @@ use tw::TweetId;
use commands::Command;
-use std::str::FromStr;
-
pub static FORGET_THREAD: Command = Command {
keyword: "forget",
params: 1,
@@ -34,13 +32,13 @@ fn remember(line: String, tweeter: &mut tw::TwitterCache, _queryer: &mut Queryer
if name.len() > 0 {
let maybe_id = TweetId::parse(line.to_owned());
match maybe_id {
- Some(twid) => {
+ Ok(twid) => {
let twete = tweeter.retrieve_tweet(&twid).unwrap().clone();
tweeter.set_thread(name.to_string(), twete.internal_id);
println!("Ok! Recorded {:?} as thread {}", twid, name);
}
- None => {
- println!("Invalid id: {}", line);
+ Err(e) => {
+ println!("Invalid id: {}", e);
}
}
}
@@ -55,12 +53,16 @@ pub static LIST_THREADS: Command = Command {
fn ls_threads(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
println!("Threads: ");
- for k in tweeter.threads() {
+ let threads: Vec<String> = tweeter.threads().collect::<Vec<&String>>().into_iter().map(|x| x.to_owned()).collect::<Vec<String>>();
+ for k in threads {
println!("Thread: {}", k);
- let latest_inner_id = tweeter.latest_in_thread(k.to_owned()).unwrap();
- if let Some(twete) = tweeter.retrieve_tweet(&TweetId::Bare(*latest_inner_id)) {
+ let latest_inner_id = tweeter.latest_in_thread(k.to_owned()).unwrap().to_owned();
+ // should be able to just directly render TweetId.. and threads should be Vec<TweetId>...
+ let twete_id_TEMP = tweeter.retrieve_tweet(&TweetId::Bare(latest_inner_id)).map(|x| x.id.to_owned());
+ if let Some(twete) = twete_id_TEMP {
// gross..
- display::render_twete(&twete.id, tweeter);
+ // and this ought to be a command to tweeter.display_info anyway...
+ display::render_twete(&TweetId::Twitter(twete), tweeter);
println!("");
} else {
println!("ERROR no tweet for remembered thread.");
diff --git a/src/commands/twete.rs b/src/commands/twete.rs
index c399df1..9f5cb0d 100644
--- a/src/commands/twete.rs
+++ b/src/commands/twete.rs
@@ -56,7 +56,7 @@ fn thread(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let id_str = text.trim();
if reply.len() > 0 {
if let Some(inner_twid) = u64::from_str(&id_str).ok() {
- if let Some(twete) = tweeter.retrieve_tweet(&TweetId::Bare(inner_twid)) {
+ if let Some(twete) = tweeter.retrieve_tweet(&TweetId::Bare(inner_twid)).map(|x| x.clone()) {
let handle = &tweeter.retrieve_user(&twete.author_id).unwrap().handle;
// TODO: definitely breaks if you change your handle right now
if handle == &tweeter.current_user.handle {
@@ -92,7 +92,8 @@ fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let id_str = text.trim();
if reply.len() > 0 {
if let Some(inner_twid) = u64::from_str(&id_str).ok() {
- if let Some(twete) = tweeter.retrieve_tweet(&TweetId::Bare(inner_twid)) {
+ // TODO: probably should just have Tweet impl Copy or something
+ if let Some(twete) = tweeter.retrieve_tweet(&TweetId::Bare(inner_twid)).map(|x| x.clone()) {
// get handles to reply to...
let author_handle = tweeter.retrieve_user(&twete.author_id).unwrap().handle.to_owned();
let mut ats: Vec<String> = twete.get_mentions(); //std::collections::HashSet::new();
@@ -103,13 +104,12 @@ fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
*/
ats.remove_item(&author_handle);
ats.insert(0, author_handle);
- // no idea why i have to .to_owned() here --v-- what about twete.rt_tweet is a move?
- if let Some(rt_tweet) = twete.rt_tweet.to_owned().and_then(|id| tweeter.retrieve_tweet(&TweetId::Twitter(id))) {
+ if let Some(rt_tweet) = twete.rt_tweet.and_then(|id| tweeter.retrieve_tweet(&TweetId::Twitter(id))).map(|x| x.clone()) {
let rt_author_handle = tweeter.retrieve_user(&rt_tweet.author_id).unwrap().handle.to_owned();
ats.remove_item(&rt_author_handle);
ats.insert(1, rt_author_handle);
}
- if let Some(qt_tweet) = twete.quoted_tweet_id.to_owned().and_then(|id| tweeter.retrieve_tweet(&TweetId::Twitter(id))) {
+ if let Some(qt_tweet) = twete.quoted_tweet_id.and_then(|id| tweeter.retrieve_tweet(&TweetId::Twitter(id))).map(|x| x.clone()) {
// let qt_author_handle = tweeter.retrieve_user(&qt_tweet.author_id).unwrap().handle.to_owned();
// ats.remove_item(&qt_author_handle);
// ats.insert(1, qt_author_handle);
@@ -145,7 +145,7 @@ fn quote(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let id_str = text.trim();
if reply.len() > 0 {
if let Some(inner_twid) = u64::from_str(&id_str).ok() {
- if let Some(twete) = tweeter.retrieve_tweet(&TweetId::Bare(inner_twid)) {
+ if let Some(twete) = tweeter.retrieve_tweet(&TweetId::Bare(inner_twid)).map(|x| x.clone()) {
let substituted = ::url_encode(reply);
let attachment_url = ::url_encode(
&format!(
diff --git a/src/commands/view.rs b/src/commands/view.rs
index c14446a..e9b38ee 100644
--- a/src/commands/view.rs
+++ b/src/commands/view.rs
@@ -18,9 +18,10 @@ pub static VIEW: Command = Command {
fn view(line: String, tweeter: &mut tw::TwitterCache, _queryer: &mut Queryer) {
// TODO handle this unwrap
let inner_twid = u64::from_str(&line).unwrap();
- let twete = tweeter.retrieve_tweet(&TweetId::Bare(inner_twid)).unwrap();
- display::render_twete(&twete.id, tweeter);
- println!(" link: https://twitter.com/i/web/status/{}", twete.id);
+ let twete = tweeter.retrieve_tweet(&TweetId::Bare(inner_twid)).unwrap().clone();
+ tweeter.display_info.recv(display::Infos::Tweet(TweetId::Twitter(twete.id.to_owned())));
+// display::render_twete(&twete.id, tweeter);
+// println!(" link: https://twitter.com/i/web/status/{}", twete.id);
}
pub static VIEW_THREAD: Command = Command {
@@ -29,22 +30,20 @@ pub static VIEW_THREAD: Command = Command {
exec: view_tr
};
-fn view_tr(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
- // TODO handle this unwrap
+fn view_tr(line: String, mut tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
+ let mut thread: Vec<TweetId> = Vec::new();
let inner_twid = u64::from_str(&line).unwrap();
- view_tr_inner(inner_twid, tweeter, queryer);
-}
-
-fn view_tr_inner(id: u64, mut tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
- let twete: tw::tweet::Tweet = tweeter.retrieve_tweet(&TweetId::Bare(id)).unwrap().to_owned();
- if let Some(reply_id) = twete.reply_to_tweet.clone() {
- if let Some(reply_internal_id) = tweeter.fetch_tweet(&reply_id, queryer).map(|x| x.internal_id).map(|x| x.to_owned()) {
- view_tr_inner(reply_internal_id, tweeter, queryer);
- println!(" |");
- println!(" v");
- }
+ let curr_id = TweetId::Bare(inner_twid);
+ let mut maybe_next_id = tweeter.retrieve_tweet(&curr_id).and_then(|x| x.reply_to_tweet.to_owned());
+ thread.push(curr_id);
+ while let Some(next_id) = maybe_next_id {
+ let curr_id = TweetId::Twitter(next_id);
+ maybe_next_id = tweeter.retrieve_tweet(&curr_id).and_then(|x| x.reply_to_tweet.to_owned());
+ thread.push(curr_id);
}
- display::render_twete(&twete.id, tweeter);
+
+ tweeter.display_info.recv(display::Infos::Thread(thread));
+// display::render_twete(&twete.id, tweeter);
// println!("link: https://twitter.com/i/web/status/{}", twete.id);
}
diff --git a/src/display/mod.rs b/src/display/mod.rs
index e7f2454..fdb8448 100644
--- a/src/display/mod.rs
+++ b/src/display/mod.rs
@@ -8,6 +8,39 @@ use ::tw::TweetId;
use std;
+pub enum Infos {
+ Tweet(TweetId),
+ Thread(Vec<TweetId>),
+ Event(tw::events::Event),
+ DM(String)
+}
+
+pub fn paint(tweeter: &mut ::tw::TwitterCache) {
+ println!("Painting, totally.");
+ println!("Statuses:");
+ {
+ let to_show = tweeter.display_info.log.iter().rev().take(4).collect::<Vec<&String>>().into_iter().rev();
+ for line in to_show {
+ println!("{}", line);
+ }
+ }
+
+ if let Some(elem) = tweeter.display_info.infos.pop() {
+ match elem {
+ Infos::Tweet(id) => render_twete(&id, tweeter),
+ Infos::Thread(ids) => {
+ println!("I'd show a thread if I knew how");
+ },
+ Infos::Event(e) => {
+ e.render(tweeter);
+ },
+ Infos::DM(msg) => {
+ println!("DM: {}", msg);
+ }
+ }
+ }
+}
+
fn color_for(handle: &String) -> termion::color::Fg<&color::Color> {
let color_map: Vec<&color::Color> = vec![
&color::Blue,
@@ -30,23 +63,25 @@ fn color_for(handle: &String) -> termion::color::Fg<&color::Color> {
}
pub trait Render {
- fn render(self, tweeter: &::tw::TwitterCache);
+ fn render(self, tweeter: &mut ::tw::TwitterCache);
}
impl Render for tw::events::Event {
- fn render(self, tweeter: &::tw::TwitterCache) {
+ fn render(self, tweeter: &mut ::tw::TwitterCache) {
match self {
tw::events::Event::Quoted { user_id, twete_id } => {
println!("---------------------------------");
- let user = tweeter.retrieve_user(&user_id).unwrap();
- println!(" quoted_tweet : {} (@{})", user.name, user.handle);
- render_twete(&twete_id, tweeter);
+ {
+ let user = tweeter.retrieve_user(&user_id).unwrap();
+ println!(" quoted_tweet : {} (@{})", user.name, user.handle);
+ }
+ render_twete(&TweetId::Twitter(twete_id), tweeter);
}
tw::events::Event::Deleted { user_id, twete_id } => {
- if let Some(handle) = tweeter.retrieve_user(&user_id).map(|x| &x.handle) {
- if let Some(_tweet) = tweeter.retrieve_tweet(&TweetId::Twitter(twete_id.to_owned())) {
+ if let Some(handle) = tweeter.retrieve_user(&user_id).map(|x| &x.handle).map(|x| x.clone()) {
+ if let Some(_tweet) = tweeter.retrieve_tweet(&TweetId::Twitter(twete_id.to_owned())).map(|x| x.clone()) {
println!("-------------DELETED------------------");
- render_twete(&twete_id, tweeter);
+ render_twete(&TweetId::Twitter(twete_id), tweeter);
println!("-------------DELETED------------------");
} else {
println!("dunno what, but do know who: {} - {}", user_id, handle);
@@ -58,9 +93,11 @@ impl Render for tw::events::Event {
},
tw::events::Event::RT_RT { user_id, twete_id } => {
println!("---------------------------------");
+ {
let user = tweeter.retrieve_user(&user_id).unwrap();
println!(" +rt_rt : {} (@{})", user.name, user.handle);
- render_twete(&twete_id, tweeter);
+ }
+ render_twete(&TweetId::Twitter(twete_id), tweeter);
},
tw::events::Event::Fav_RT { user_id, twete_id } => {
println!("---------------------------------");
@@ -69,19 +106,23 @@ impl Render for tw::events::Event {
} else {
println!(" +rt_fav but don't know who {} is", user_id);
}
- render_twete(&twete_id, tweeter);
+ render_twete(&TweetId::Twitter(twete_id), tweeter);
},
tw::events::Event::Fav { user_id, twete_id } => {
println!("---------------------------------");
+ {
let user = tweeter.retrieve_user(&user_id).unwrap();
println!("{} +fav : {} (@{}){}", color::Fg(color::Yellow), user.name, user.handle, color::Fg(color::Reset));
- render_twete(&twete_id, tweeter);
+ }
+ render_twete(&TweetId::Twitter(twete_id), tweeter);
},
tw::events::Event::Unfav { user_id, twete_id } => {
println!("---------------------------------");
+ {
let user = tweeter.retrieve_user(&user_id).unwrap();
println!("{} -fav : {} (@{}){}", color::Fg(color::Yellow), user.name, user.handle, color::Fg(color::Reset));
- render_twete(&twete_id, tweeter);
+ }
+ render_twete(&TweetId::Twitter(twete_id), tweeter);
},
tw::events::Event::Followed { user_id } => {
println!("---------------------------------");
@@ -102,22 +143,22 @@ impl Render for tw::events::Event {
}
}
-pub fn render_twete(twete_id: &String, tweeter: &tw::TwitterCache) {
+pub fn render_twete(twete_id: &TweetId, tweeter: &mut tw::TwitterCache) {
let id_color = color::Fg(color::Rgb(180, 80, 40));
- let maybe_twete = tweeter.retrieve_tweet(&TweetId::Twitter(twete_id.to_owned()));
+ let maybe_twete = tweeter.retrieve_tweet(twete_id).map(|x| x.clone());
if maybe_twete.is_none() {
- println!("No such tweet: {}", twete_id);
+ println!("No such tweet: {:?}", twete_id);
return;
}
let twete = maybe_twete.unwrap();
// if we got the tweet, the API gave us the user too
- let user = tweeter.retrieve_user(&twete.author_id).unwrap();
+ let user = tweeter.retrieve_user(&twete.author_id).map(|x| x.clone()).unwrap();
match twete.rt_tweet {
Some(ref rt_id) => {
// same for a retweet
- let rt = tweeter.retrieve_tweet(&TweetId::Twitter(rt_id.to_owned())).unwrap();
+ let rt = tweeter.retrieve_tweet(&TweetId::Twitter(rt_id.to_owned())).unwrap().clone();
// and its author
- let rt_author = tweeter.retrieve_user(&rt.author_id).unwrap();
+ let rt_author = tweeter.retrieve_user(&rt.author_id).unwrap().clone();
println!("{} id:{} (rt_id:{}){}{}",
id_color, rt.internal_id, twete.internal_id,
rt.reply_to_tweet.clone()
@@ -156,8 +197,9 @@ pub fn render_twete(twete_id: &String, tweeter: &tw::TwitterCache) {
println!(" {}", twete.text.replace("\r", "\\r").split("\n").collect::<Vec<&str>>().join("\n "));
if let Some(ref qt_id) = twete.quoted_tweet_id {
- if let Some(ref qt) = tweeter.retrieve_tweet(&TweetId::Twitter(qt_id.to_owned())) {
- let qt_author = tweeter.retrieve_user(&qt.author_id).unwrap();
+ let maybe_qt = tweeter.retrieve_tweet(&TweetId::Twitter(qt_id.to_owned())).map(|x| x.to_owned());
+ if let Some(qt) = maybe_qt {
+ let qt_author = tweeter.retrieve_user(&qt.author_id).unwrap().clone();
println!("{} id:{}{}{}",
id_color, qt.internal_id,
qt.reply_to_tweet.clone()
diff --git a/src/main.rs b/src/main.rs
index e53e6b6..4352fe9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -28,6 +28,7 @@ use linestream::LineStream;
mod tw;
mod display;
+mod commands;
//Change these values to your real Twitter API credentials
static consumer_key: &str = "T879tHWDzd6LvKWdYVfbJL4Su";
@@ -258,7 +259,7 @@ fn do_ui(ui_rx_orig: chan::Receiver<Vec<u8>>, twete_rx: chan::Receiver<Vec<u8>>,
if line == "reconnect\n".as_bytes() {
return Some((ui_rx_orig.clone(), connect_twitter_stream()));
} else {
- handle_user_input(line, &mut tweeter, &mut queryer);
+ tweeter.handle_user_input(line, &mut queryer);
}
}
None => std::process::exit(0)
@@ -268,11 +269,15 @@ fn do_ui(ui_rx_orig: chan::Receiver<Vec<u8>>, twete_rx: chan::Receiver<Vec<u8>>,
},
ui_rx_a.recv() -> user_input => match user_input {
Some(line) => {
- handle_user_input(line, &mut tweeter, &mut queryer);
+ tweeter.handle_user_input(line, &mut queryer);
},
None => println!("UI thread hung up...")
}
+ // and then we can introduce a channel that just sends a message every 100 ms or so
+ // that acts as a clock!
}
+ // one day display_info should be distinct
+ display::paint(tweeter);
}
}
@@ -308,40 +313,6 @@ fn url_encode(s: &str) -> String {
.replace("]", "%5d")
}
-mod commands;
-use commands::Command;
-
-// is there a nice way to make this accept commands: Iterable<&'a Command>? eg either a Vec or an
-// array or whatever?
-// (extra: WITHOUT having to build an iterator?)
-// ((extra 2: when compiled with -O3, how does `commands` iteration look? same as array?))
-fn parse_word_command<'a, 'b>(line: &'b str, commands: &[&'a Command]) -> Option<(&'b str, &'a Command)> {
- for cmd in commands.into_iter() {
- if cmd.params == 0 {
- if line == cmd.keyword {
- return Some(("", &cmd));
- }
- } else if line.starts_with(cmd.keyword) {
- if line.find(" ").map(|x| x == cmd.keyword.len()).unwrap_or(false) {
- // let inner_twid = u64::from_str(&linestr.split(" ").collect::<Vec<&str>>()[1]).unwrap();
- return Some((line.get((cmd.keyword.len() + 1)..).unwrap().trim(), &cmd));
- }
- }
- }
- return None
-}
-
-fn handle_user_input(line: Vec<u8>, tweeter: &mut tw::TwitterCache, mut queryer: &mut Queryer) {
- let command_bare = String::from_utf8(line).unwrap();
- let command = command_bare.trim();
- if let Some((line, cmd)) = parse_word_command(&command, commands::COMMANDS) {
- (cmd.exec)(line.to_owned(), tweeter, &mut queryer);
- } else {
- println!("I don't know what {} means", command);
- }
- println!(""); // temporaryish because there's no visual distinction between output atm
-}
-
fn connect_twitter_stream() -> chan::Receiver<Vec<u8>> {
let (twete_tx, twete_rx) = chan::sync::<Vec<u8>>(0);
diff --git a/src/tw/mod.rs b/src/tw/mod.rs
index 28f4ff0..f2582cf 100644
--- a/src/tw/mod.rs
+++ b/src/tw/mod.rs
@@ -2,6 +2,7 @@ use std::path::Path;
use std::str::FromStr;
use std::fs::File;
use std::io::{BufRead, BufReader, Read};
+use std::error::Error;
extern crate chrono;
use self::chrono::prelude::*;
@@ -101,7 +102,9 @@ pub struct TwitterCache {
#[serde(skip)]
pub current_user: User,
#[serde(skip)]
- id_conversions: IdConversions
+ id_conversions: IdConversions,
+ #[serde(skip)]
+ pub display_info: DisplayInfo
}
// Internally, a monotonically increasin i64 is always the id used.
@@ -160,47 +163,31 @@ mod tests {
}
impl TweetId {
- pub fn parse(id_str: String) -> Option<TweetId> {
- // ...
+ pub fn parse(id_str: String) -> Result<TweetId, String> {
+ // TODO: figure out how to return a Result<TweetId, <.. the result types ..>>
if id_str.starts_with("twitter:") {
- Some(TweetId::Twitter(id_str.chars().skip("twitter:".len()).collect()))
+ Ok(TweetId::Twitter(id_str.chars().skip("twitter:".len()).collect()))
} else if id_str.starts_with(":") {
let rest = id_str.chars().skip(1);
- if rest.clone().all(|x| x.is_digit(10)) {
- match u64::from_str(&rest.collect::<String>()) {
- Ok(num) => Some(TweetId::Bare(num)),
- Err(e) => {
- println!("Invalid id: {} - {}", id_str, e);
- None
- }
- }
- } else {
- None
- }
+ u64::from_str(&rest.collect::<String>())
+ .map(TweetId::Bare)
+ .map_err(|err| err.description().to_string())
} else if id_str.find(":") == Some(8) {
let strings: Vec<&str> = id_str.split(":").collect();
- if strings.len() == 2 {
- match (strings[0].chars().all(|x| x.is_digit(10)), u64::from_str(strings[1])) {
- (true, Ok(num)) => Some(TweetId::Dated(strings[0].to_owned(), num)),
- _ => {
- println!("Invalid format.");
- None
- }
- }
+ if strings.len() == 2 && strings[0].chars().all(|x| x.is_digit(10)) {
+ u64::from_str(strings[1])
+ .map(|id| TweetId::Dated(strings[0].to_owned(), id))
+ .map_err(|err| err.description().to_string())
} else {
- None
+ Err("Invalid format, date and id must be all numbers".to_string())
}
} else if id_str.chars().all(|x| x.is_digit(10)) {
// today
- match u64::from_str(&id_str) {
- Ok(num) => Some(TweetId::Today(num)),
- Err(e) => {
- println!("Invalid id: {} - {}", id_str, e);
- None
- }
- }
+ u64::from_str(&id_str)
+ .map(TweetId::Today)
+ .map_err(|err| err.description().to_string())
} else {
- None
+ Err(format!("Unrecognized id string: {}", id_str))
}
}
}
@@ -211,7 +198,10 @@ impl IdConversions {
//
// except in the TweetId::Twitter case we TweetId -> Option<Tweet> -> Option<u64> ... to ->
// Option<Tweet> in the future?
- fn to_inner_id(&self, tweeter: &TwitterCache, twid: TweetId) -> Option<u64> {
+ // // WHY must we take mutable borrow of TwitterCache here, you ask?
+ // // well, because it contains display_info, and retrieve_tweet can
+ // // end up logging, for now!
+ fn to_inner_id(&self, tweeter: &mut TwitterCache, twid: TweetId) -> Option<u64> {
match twid {
TweetId::Today(num) => {
let first_for_today: u64 = 0;
@@ -227,6 +217,54 @@ impl IdConversions {
}
}
+pub struct DisplayInfo {
+ pub log: Vec<String>,
+ pub infos: Vec<display::Infos>
+}
+
+impl Default for DisplayInfo {
+ fn default() -> Self {
+ DisplayInfo {
+ log: Vec::new(),
+ infos: Vec::new()
+ }
+ }
+}
+
+impl DisplayInfo {
+ fn status(&mut self, stat: String) {
+ self.log.push(stat);
+ }
+
+ pub fn recv(&mut self, info: display::Infos) {
+ self.infos.push(info);
+ }
+}
+
+use commands::Command;
+use Queryer;
+
+// TODO:
+// is there a nice way to make this accept commands: Iterable<&'a Command>? eg either a Vec or an
+// array or whatever?
+// (extra: WITHOUT having to build an iterator?)
+// ((extra 2: when compiled with -O3, how does `commands` iteration look? same as array?))
+fn parse_word_command<'a, 'b>(line: &'b str, commands: &[&'a Command]) -> Option<(&'b str, &'a Command)> {
+ for cmd in commands.into_iter() {
+ if cmd.params == 0 {
+ if line == cmd.keyword {
+ return Some(("", &cmd));
+ }
+ } else if line.starts_with(cmd.keyword) {
+ if line.find(" ").map(|x| x == cmd.keyword.len()).unwrap_or(false) {
+ // let inner_twid = u64::from_str(&linestr.split(" ").collect::<Vec<&str>>()[1]).unwrap();
+ return Some((line.get((cmd.keyword.len() + 1)..).unwrap().trim(), &cmd));
+ }
+ }
+ }
+ return None
+}
+
impl TwitterCache {
const PROFILE_DIR: &'static str = "cache/";
const TWEET_CACHE: &'static str = "cache/tweets.json";
@@ -246,9 +284,24 @@ impl TwitterCache {
caching_permitted: true,
current_user: User::default(),
threads: HashMap::new(),
- id_conversions: IdConversions::default()
+ id_conversions: IdConversions::default(),
+ display_info: DisplayInfo::default()
+ }
+ }
+
+ // TODO: pull out the "Cache" part of TwitterCache, it can be serialized/deserialized - the
+ // rest of the history is just for the running instance..
+ pub fn handle_user_input(&mut self, line: Vec<u8>, mut queryer: &mut Queryer) {
+ let command_bare = String::from_utf8(line).unwrap();
+ let command = command_bare.trim();
+ if let Some((line, cmd)) = parse_word_command(&command, ::commands::COMMANDS) {
+ (cmd.exec)(line.to_owned(), self, &mut queryer);
+ } else {
+ self.display_info.status(format!("I don't know what {} means", command).to_string());
}
+// println!(""); // temporaryish because there's no visual distinction between output atm
}
+
fn new_without_caching() -> TwitterCache {
let mut cache = TwitterCache::new();
cache.caching_permitted = false;
@@ -327,19 +380,22 @@ impl TwitterCache {
}
Err(e) => {
// TODO! should be able to un-frick profile after startup.
- println!("Error reading profile, profile caching disabled... {}", e);
- TwitterCache::new_without_caching()
+ let mut cache = TwitterCache::new_without_caching();
+ cache.display_info.status(format!("Error reading profile, profile caching disabled... {}", e));
+ cache
}
}
}
Err(e) => {
- println!("Error reading cached profile: {}. Profile caching disabled.", e);
- TwitterCache::new_without_caching()
+ let mut cache = TwitterCache::new_without_caching();
+ cache.display_info.status(format!("Error reading cached profile: {}. Profile caching disabled.", e));
+ cache
}
}
} else {
- println!("Hello! First time setup?");
- TwitterCache::new()
+ let mut cache = TwitterCache::new();
+ cache.display_info.status(format!("Hello! First time setup?"));
+ cache
}
}
pub fn cache_api_tweet(&mut self, json: serde_json::Value) {
@@ -421,19 +477,22 @@ impl TwitterCache {
/* nothing else to care about now, i think? */
}
}
- pub fn retrieve_tweet(&self, tweet_id: &TweetId) -> Option<&Tweet> {
+ pub fn retrieve_tweet(&mut self, tweet_id: &TweetId) -> Option<&Tweet> {
match tweet_id {
&TweetId::Bare(ref id) => {
- self.id_conversions.id_to_tweet_id.get(id)
- .and_then(|x| self.tweets.get(x))
+ let maybe_tweet_id = self.id_conversions.id_to_tweet_id.get(id);
+ match maybe_tweet_id {
+ Some(id) => self.tweets.get(id),
+ None => None
+ }
},
&TweetId::Today(ref id) => {
let inner_id = self.id_conversions.id_to_tweet_id.get(id);
- panic!("Retrieving tweets with dated IDs is not yet supported.");
+ self.display_info.status("Retrieving tweets with dated IDs is not yet supported.".to_string());
None
},
&TweetId::Dated(ref date, ref id) => {
- panic!("Retrieving tweets with dated IDs is not yet supported.");
+ self.display_info.status("Retrieving tweets with dated IDs is not yet supported.".to_string());
None
},
&TweetId::Twitter(ref id) => self.tweets.get(id)
@@ -446,7 +505,7 @@ impl TwitterCache {
if !self.tweets.contains_key(tweet_id) {
match self.look_up_tweet(tweet_id, &mut queryer) {
Some(json) => self.cache_api_tweet(json),
- None => println!("Unable to retrieve tweet {}", tweet_id)
+ None => self.display_info.status(format!("Unable to retrieve tweet {}", tweet_id))
};
}
self.tweets.get(tweet_id)
@@ -456,8 +515,8 @@ impl TwitterCache {
let maybe_parsed = self.look_up_user(user_id, &mut queryer).and_then(|x| User::from_json(x));
match maybe_parsed {
Some(tw) => self.cache_user(tw),
- None => println!("Unable to retrieve user {}", user_id)
- };
+ None => self.display_info.status(format!("Unable to retrieve user {}", user_id))
+ }
}
self.users.get(user_id)
}
@@ -567,18 +626,22 @@ fn handle_twitter_event(
mut queryer: &mut ::Queryer) {
tweeter.cache_api_event(structure.clone(), &mut queryer);
if let Some(event) = events::Event::from_json(structure) {
- event.render(&tweeter);
- };
+ tweeter.display_info.recv(display::Infos::Event(event));
+ } else {
+ // ought to handle the None case...
+ }
}
fn handle_twitter_delete(
structure: serde_json::Map<String, serde_json::Value>,
tweeter: &mut TwitterCache,
_queryer: &mut ::Queryer) {
- events::Event::Deleted {
- user_id: structure["delete"]["status"]["user_id_str"].as_str().unwrap().to_string(),
- twete_id: structure["delete"]["status"]["id_str"].as_str().unwrap().to_string()
- }.render(tweeter);
+ tweeter.display_info.recv(display::Infos::Event(
+ events::Event::Deleted {
+ user_id: structure["delete"]["status"]["user_id_str"].as_str().unwrap().to_string(),
+ twete_id: structure["delete"]["status"]["id_str"].as_str().unwrap().to_string()
+ }
+ ));
}
fn handle_twitter_twete(
@@ -587,16 +650,16 @@ fn handle_twitter_twete(
_queryer: &mut ::Queryer) {
let twete_id = structure["id_str"].as_str().unwrap().to_string();
tweeter.cache_api_tweet(serde_json::Value::Object(structure));
- display::render_twete(&twete_id, tweeter);
+ tweeter.display_info.recv(display::Infos::Tweet(TweetId::Twitter(twete_id)));
+ // display::render_twete(&twete_id, tweeter);
}
fn handle_twitter_dm(
structure: serde_json::Map<String, serde_json::Value>,
- _tweeter: &mut TwitterCache,
+ tweeter: &mut TwitterCache,
_queryer: &mut ::Queryer) {
// show DM
- println!("{}", structure["direct_message"]["text"].as_str().unwrap());
- println!("Unknown struture {:?}", structure);
+ tweeter.display_info.recv(display::Infos::DM(structure["direct_message"]["text"].as_str().unwrap().to_string()));
}
fn handle_twitter_welcome(
@@ -615,9 +678,9 @@ fn handle_twitter_welcome(
handle: my_name.to_owned(),
name: my_name.to_owned()
};
- println!("You are {}", tweeter.current_user.handle);
+ tweeter.display_info.status(format!("You are {}", tweeter.current_user.handle))
} else {
- println!("Unable to make API call to figure out who you are...");
+ tweeter.display_info.status("Unable to make API call to figure out who you are...".to_string());
}
let followers = tweeter.get_followers(queryer).unwrap();
let id_arr: Vec<String> = followers["ids"].as_array().unwrap().iter().map(|x| x.as_str().unwrap().to_owned()).collect();
@@ -642,7 +705,7 @@ pub fn handle_message(
} else if objmap.contains_key("direct_message") {
handle_twitter_dm(objmap, tweeter, queryer);
}
- println!("");
+// self.display_info.status("");
},
_ => ()
};
diff --git a/src/tw/user.rs b/src/tw/user.rs
index 1da82f0..8f41b6d 100644
--- a/src/tw/user.rs
+++ b/src/tw/user.rs
@@ -1,6 +1,6 @@
extern crate serde_json;
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct User {
pub id: String,
pub name: String,