aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2017-11-01 04:31:43 -0700
committeriximeow <me@iximeow.net>2017-11-01 04:31:43 -0700
commit96425724b0e435c33876f4dd9685aed2bbdea1fd (patch)
treedd48118f712344d5dcea278148f3c82996f74021 /src
parentc40aa2bf95131317e2a9b199afd0b34ae2d847ee (diff)
move more logging to be through DisplayInfo statuses
move DisplayInfo into ... display flush output, duh
Diffstat (limited to 'src')
-rw-r--r--src/commands/fav.rs14
-rw-r--r--src/commands/follow.rs7
-rw-r--r--src/commands/look_up.rs7
-rw-r--r--src/commands/quit.rs2
-rw-r--r--src/commands/show_cache.rs18
-rw-r--r--src/commands/thread.rs15
-rw-r--r--src/commands/twete.rs67
-rw-r--r--src/commands/view.rs13
-rw-r--r--src/display/mod.rs98
-rw-r--r--src/main.rs36
-rw-r--r--src/tw/events.rs10
-rw-r--r--src/tw/mod.rs75
-rw-r--r--src/tw/tweet.rs17
-rw-r--r--src/tw/user.rs39
14 files changed, 225 insertions, 193 deletions
diff --git a/src/commands/fav.rs b/src/commands/fav.rs
index 89e1987..6109310 100644
--- a/src/commands/fav.rs
+++ b/src/commands/fav.rs
@@ -19,13 +19,16 @@ fn unfav(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
match maybe_id {
Ok(twid) => {
if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self
- queryer.do_api_post(&format!("{}?id={}", UNFAV_TWEET_URL, twete.id));
+ match queryer.do_api_post(&format!("{}?id={}", UNFAV_TWEET_URL, twete.id)) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
+ }
} else {
tweeter.display_info.status(format!("No tweet for id: {:?}", twid));
}
}
Err(e) => {
- println!("Invalid id: {}", e);
+ tweeter.display_info.status(format!("Invalid id: {}", e));
}
}
}
@@ -42,13 +45,16 @@ fn fav(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
Ok(twid) => {
// tweeter.to_twitter_tweet_id(twid)...
if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self
- queryer.do_api_post(&format!("{}?id={}", FAV_TWEET_URL, twete.id));
+ match queryer.do_api_post(&format!("{}?id={}", FAV_TWEET_URL, twete.id)) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
+ }
} else {
tweeter.display_info.status(format!("No tweet for id: {:?}", twid));
}
}
Err(e) => {
- println!("Invalid id: {}", e);
+ tweeter.display_info.status(format!("Invalid id: {}", e));
}
}
}
diff --git a/src/commands/follow.rs b/src/commands/follow.rs
index ad121e5..b0dc8a7 100644
--- a/src/commands/follow.rs
+++ b/src/commands/follow.rs
@@ -14,7 +14,10 @@ pub static UNFOLLOW: Command = Command {
fn unfl(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let screen_name = line.trim();
- queryer.do_api_post(&format!("{}?screen_name={}", FOLLOW_URL, screen_name));
+ match queryer.do_api_post(&format!("{}?screen_name={}", FOLLOW_URL, screen_name)) {
+ Ok(_resp) => (),
+ Err(e) => tweeter.display_info.status(format!("unfl request error: {}", e))
+ }
}
pub static FOLLOW: Command = Command {
@@ -25,5 +28,5 @@ pub static FOLLOW: Command = Command {
fn fl(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let screen_name = line.trim();
- println!("fl resp: {:?}", queryer.do_api_post(&format!("{}?screen_name={}", UNFOLLOW_URL, screen_name)));
+ tweeter.display_info.status(format!("fl resp: {:?}", queryer.do_api_post(&format!("{}?screen_name={}", UNFOLLOW_URL, screen_name))));
}
diff --git a/src/commands/look_up.rs b/src/commands/look_up.rs
index 701ce2d..dff56aa 100644
--- a/src/commands/look_up.rs
+++ b/src/commands/look_up.rs
@@ -12,10 +12,11 @@ pub static LOOK_UP_USER: Command = Command {
};
fn look_up_user(line: String, tweeter: &mut tw::TwitterCache, mut queryer: &mut Queryer) {
- if let Some(user) = tweeter.fetch_user(&line, &mut queryer) {
- println!("{:?}", user);
+ // should probably just pass the id?
+ if let Some(user) = tweeter.fetch_user(&line, &mut queryer).map(|x| x.clone()) {
+ tweeter.display_info.recv(display::Infos::User(user));
} else {
-// println!("Couldn't retrieve {}", userid);
+ tweeter.display_info.status(format!("Couldn't retrieve {}", line));
}
}
diff --git a/src/commands/quit.rs b/src/commands/quit.rs
index 982c48f..716c412 100644
--- a/src/commands/quit.rs
+++ b/src/commands/quit.rs
@@ -12,7 +12,7 @@ pub static QUIT: Command = Command {
};
fn quit(_line: String, tweeter: &mut tw::TwitterCache, _queryer: &mut Queryer) {
- println!("Bye bye!");
+ tweeter.display_info.status("Bye bye!".to_owned());
tweeter.store_cache();
exit(0);
}
diff --git a/src/commands/show_cache.rs b/src/commands/show_cache.rs
index 3c31697..59ecfc2 100644
--- a/src/commands/show_cache.rs
+++ b/src/commands/show_cache.rs
@@ -9,23 +9,23 @@ pub static SHOW_CACHE: Command = Command {
exec: show_cache
};
-fn show_cache(line: String, tweeter: &mut tw::TwitterCache, mut queryer: &mut Queryer) {
- println!("----* USERS *----");
+fn show_cache(_line: String, tweeter: &mut tw::TwitterCache, mut queryer: &mut Queryer) {
+ tweeter.display_info.status("----* USERS *----".to_owned());
for (uid, user) in &tweeter.users {
- println!("User: {} -> {:?}", uid, user);
+ tweeter.display_info.status(format!("User: {} -> {:?}", uid, user));
}
- println!("----* TWEETS *----");
+ tweeter.display_info.status("----* TWEETS *----".to_owned());
for (tid, tweet) in &tweeter.tweets {
- println!("Tweet: {} -> {:?}", tid, tweet);
+ tweeter.display_info.status(format!("Tweet: {} -> {:?}", tid, tweet));
}
- println!("----* FOLLOWERS *----");
+ tweeter.display_info.status("----* FOLLOWERS *----".to_owned());
for uid in &tweeter.followers.clone() {
- let user_res = tweeter.fetch_user(uid, &mut queryer);
+ let user_res = tweeter.fetch_user(uid, &mut queryer).map(|x| x.clone());
match user_res {
Some(user) => {
- println!("Follower: {} - {:?}", uid, user);
+ tweeter.display_info.status(format!("Follower: {} - {:?}", uid, user));
}
- None => { println!(" ..."); }
+ None => { tweeter.display_info.status(" ...".to_owned()); }
}
}
}
diff --git a/src/commands/thread.rs b/src/commands/thread.rs
index 8880af7..6f05048 100644
--- a/src/commands/thread.rs
+++ b/src/commands/thread.rs
@@ -14,7 +14,7 @@ pub static FORGET_THREAD: Command = Command {
fn forget(line: String, tweeter: &mut tw::TwitterCache, _queryer: &mut Queryer) {
tweeter.forget_thread(line.trim().to_string());
- println!("Ok! Forgot thread {}", line.trim().to_string());
+ tweeter.display_info.status(format!("Ok! Forgot thread {}", line.trim().to_string()));
}
pub static REMEMBER_THREAD: Command = Command {
@@ -55,20 +55,9 @@ pub static LIST_THREADS: Command = Command {
};
fn ls_threads(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
- println!("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().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..
- // 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.");
- }
+ tweeter.display_info.recv(display::Infos::TweetWithContext(TweetId::Bare(latest_inner_id), format!("Thread: {}", k)))
}
}
diff --git a/src/commands/twete.rs b/src/commands/twete.rs
index eb21a15..f057e5f 100644
--- a/src/commands/twete.rs
+++ b/src/commands/twete.rs
@@ -5,8 +5,6 @@ use tw::TweetId;
use commands::Command;
-use std::str::FromStr;
-
static DEL_TWEET_URL: &str = "https://api.twitter.com/1.1/statuses/destroy";
static RT_TWEET_URL: &str = "https://api.twitter.com/1.1/statuses/retweet";
static CREATE_TWEET_URL: &str = "https://api.twitter.com/1.1/statuses/update.json";
@@ -22,13 +20,16 @@ fn del(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
Ok(twid) => {
// TODO this really converts twid to a TweetId::Twitter
if let Some(twitter_id) = tweeter.retrieve_tweet(&twid).map(|x| x.id.to_owned()) {
- queryer.do_api_post(&format!("{}/{}.json", DEL_TWEET_URL, twitter_id));
+ match queryer.do_api_post(&format!("{}/{}.json", DEL_TWEET_URL, twitter_id)) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
+ }
} else {
tweeter.display_info.status(format!("No tweet for id {:?}", twid));
}
},
Err(e) => {
- tweeter.display_info.status(format!("Invalid id: {:?}", line));
+ tweeter.display_info.status(format!("Invalid id: {:?} ({})", line, e));
}
}
}
@@ -39,17 +40,19 @@ pub static TWETE: Command = Command {
exec: twete
};
-fn twete(line: String, _tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
+fn twete(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let text = line.trim();
let substituted = ::url_encode(text);
- println!("msg len: {}", text.len());
- println!("excessively long? {}", text.len() > 140);
- if text.len() > 140 {
- queryer.do_api_post(&format!("{}?status={}", CREATE_TWEET_URL, substituted));
+ if text.len() <= 140 {
+ match queryer.do_api_post(&format!("{}?status={}", CREATE_TWEET_URL, substituted)) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
+ }
} else {
- queryer.do_api_post(&format!("{}?status={}&weighted_character_count=true", CREATE_TWEET_URL, substituted));
+ // TODO: this 140 is maybe sometimes 280.. :)
+ // and see if weighted_character_count still does things?
+ tweeter.display_info.status(format!("tweet is too long: {}/140 chars", text.len()));
}
-// println!("{}", &format!("{}?status={}", CREATE_TWEET_URL, substituted));
}
pub static THREAD: Command = Command {
@@ -69,13 +72,16 @@ fn thread(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
match maybe_id {
Ok(twid) => {
if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self
- let handle = &tweeter.retrieve_user(&twete.author_id).unwrap().handle;
+ let handle = &tweeter.retrieve_user(&twete.author_id).unwrap().handle.to_owned();
// TODO: definitely breaks if you change your handle right now
if handle == &tweeter.current_user.handle {
let substituted = ::url_encode(reply);
- queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id));
+ match queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id)) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
+ }
} else {
- println!("you can only thread your own tweets");
+ tweeter.display_info.status("you can only thread your own tweets".to_owned());
// ask if it should .@ instead?
}
}
@@ -85,10 +91,10 @@ fn thread(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
}
}
} else {
- println!("thread <id> your sik reply");
+ tweeter.display_info.status("thread <id> your sik reply".to_owned());
}
} else {
- println!("thread <id> your sik reply");
+ tweeter.display_info.status("thread <id> your sik reply".to_owned());
}
}
@@ -129,21 +135,23 @@ fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let decorated_ats: Vec<String> = ats.into_iter().map(|x| format!("@{}", x)).collect();
let full_reply = format!("{} {}", decorated_ats.join(" "), reply);
let substituted = ::url_encode(&full_reply);
- // println!("{}", (&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id)));
- queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id));
+ match queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id)) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
+ }
} else {
tweeter.display_info.status(format!("No tweet for id: {:?}", twid));
}
},
Err(e) => {
- tweeter.display_info.status(format!("Cannot parse input: {:?}", id_str));
+ tweeter.display_info.status(format!("Cannot parse input: {:?} ({})", id_str, e));
}
}
} else {
- println!("rep <id> your sik reply");
+ tweeter.display_info.status("rep <id> your sik reply".to_owned());
}
} else {
- println!("rep <id> your sik reply");
+ tweeter.display_info.status("rep <id> your sik reply".to_owned());
}
}
@@ -172,14 +180,16 @@ fn quote(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
twete.id
)
);
- println!("{}", substituted);
- queryer.do_api_post(
+ match queryer.do_api_post(
&format!("{}?status={}&attachment_url={}",
CREATE_TWEET_URL,
substituted,
attachment_url
)
- );
+ ) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
+ }
} else {
tweeter.display_info.status(format!("No tweet found for id {:?}", twid));
}
@@ -189,10 +199,10 @@ fn quote(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
}
}
} else {
- println!("rep <id> your sik reply");
+ tweeter.display_info.status("rep <id> your sik reply".to_owned());
}
} else {
- println!("rep <id> your sik reply");
+ tweeter.display_info.status("rep <id> your sik reply".to_owned());
}
}
@@ -207,7 +217,10 @@ fn retwete(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer)
Ok(twid) => {
// TODO this really converts twid to a TweetId::Twitter
if let Some(twitter_id) = tweeter.retrieve_tweet(&twid).map(|x| x.id.to_owned()) {
- queryer.do_api_post(&format!("{}/{}.json", RT_TWEET_URL, twitter_id));
+ match queryer.do_api_post(&format!("{}/{}.json", RT_TWEET_URL, twitter_id)) {
+ Ok(_) => (),
+ Err(e) => tweeter.display_info.status(e)
+ }
} else {
tweeter.display_info.status(format!("No tweet for id {:?}", twid));
}
diff --git a/src/commands/view.rs b/src/commands/view.rs
index a6ce647..0c9e974 100644
--- a/src/commands/view.rs
+++ b/src/commands/view.rs
@@ -5,8 +5,6 @@ use tw::TweetId;
use commands::Command;
-use std::str::FromStr;
-
use display;
pub static VIEW: Command = Command {
@@ -19,7 +17,10 @@ fn view(line: String, tweeter: &mut tw::TwitterCache, _queryer: &mut Queryer) {
match TweetId::parse(line) {
Ok(twid) => {
if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) {
- tweeter.display_info.recv(display::Infos::Tweet(TweetId::Twitter(twete.id.to_owned())));
+ tweeter.display_info.recv(display::Infos::TweetWithContext(
+ TweetId::Twitter(twete.id.to_owned()),
+ format!("link: https://twitter.com/i/web/status/{}", twete.id)
+ ));
} else {
tweeter.display_info.status(format!("No tweet for id {:?}", twid));
}
@@ -28,8 +29,6 @@ fn view(line: String, tweeter: &mut tw::TwitterCache, _queryer: &mut Queryer) {
tweeter.display_info.status(format!("Invalid id {:?}", e));
}
}
-// display::render_twete(&twete.id, tweeter);
-// println!(" link: https://twitter.com/i/web/status/{}", twete.id);
}
pub static VIEW_THREAD: Command = Command {
@@ -60,8 +59,6 @@ fn view_tr(line: String, mut tweeter: &mut tw::TwitterCache, queryer: &mut Query
}
tweeter.display_info.recv(display::Infos::Thread(thread));
-// display::render_twete(&twete.id, tweeter);
-// println!("link: https://twitter.com/i/web/status/{}", twete.id);
}
pub static VIEW_THREAD_FORWARD: Command = Command {
@@ -70,7 +67,7 @@ pub static VIEW_THREAD_FORWARD: Command = Command {
exec: view_tr_forward
};
-fn view_tr_forward(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
+fn view_tr_forward(_line: String, _tweeter: &mut tw::TwitterCache, _queryer: &mut Queryer) {
// first see if we have a thread for the tweet named
// if we do not, we'll have to mimic a request like
// curl 'https://twitter.com/jojonila/status/914383908090691584' \
diff --git a/src/display/mod.rs b/src/display/mod.rs
index 4faaf5b..73a1e09 100644
--- a/src/display/mod.rs
+++ b/src/display/mod.rs
@@ -15,45 +15,91 @@ use std;
#[derive(Clone)]
pub enum Infos {
Tweet(TweetId),
+ TweetWithContext(TweetId, String),
Thread(Vec<TweetId>),
Event(tw::events::Event),
- DM(String)
+ DM(String),
+ User(tw::user::User)
}
-pub fn paint(tweeter: &mut ::tw::TwitterCache) {
+pub struct DisplayInfo {
+ pub log_seek: u32,
+ pub infos_seek: u32,
+ pub log: Vec<String>,
+ pub infos: Vec<Infos>
+}
+
+impl Default for DisplayInfo {
+ fn default() -> Self {
+ DisplayInfo {
+ log_seek: 0,
+ infos_seek: 0,
+ log: Vec::new(),
+ infos: Vec::new()
+ }
+ }
+}
+
+impl DisplayInfo {
+ pub fn status(&mut self, stat: String) {
+ self.log.push(stat);
+ }
+
+ pub fn recv(&mut self, info: Infos) {
+ self.infos.push(info);
+ }
+}
+
+pub fn paint(tweeter: &mut ::tw::TwitterCache) -> Result<(), std::io::Error> {
match termion::terminal_size() {
- Ok((width, height)) => {
+ Ok((_width, height)) => {
// draw input prompt
- println!("{}{}", cursor::Goto(1, height - 6), clear::CurrentLine);
- println!("{}{}>", cursor::Goto(1, height - 5), clear::CurrentLine);
- println!("{}{}", cursor::Goto(1, height - 4), clear::CurrentLine);
+ print!("{}{}", cursor::Goto(1, height - 6), clear::CurrentLine);
+ print!("{}{}>", cursor::Goto(1, height - 5), clear::CurrentLine);
+ print!("{}{}", cursor::Goto(1, height - 4), clear::CurrentLine);
let mut i = 0;
let log_size = 4;
let last_elem = tweeter.display_info.log.len().saturating_sub(log_size);
{
- let to_show = tweeter.display_info.log.drain(last_elem..);
+ let to_show = tweeter.display_info.log[last_elem..].iter().rev();
for line in to_show {
- println!("{}{}{}", cursor::Goto(1, height - i), clear::CurrentLine, line);
+ print!("{}{}{}/{}: {}", cursor::Goto(1, height - i), clear::CurrentLine, tweeter.display_info.log.len() - 1 - i as usize, tweeter.display_info.log.len() - 1, line);
i = i + 1;
}
}
while i < log_size as u16 {
- println!("{}{}", cursor::Goto(1, height - i), clear::CurrentLine);
+ print!("{}{}", cursor::Goto(1, height - i), clear::CurrentLine);
i = i + 1;
}
// draw status lines
// draw tweets
- let last_twevent = tweeter.display_info.infos.len().saturating_sub(height as usize - 4);
+ let last_twevent = tweeter.display_info.infos.len().saturating_sub(height as usize - 4).saturating_sub(tweeter.display_info.infos_seek as usize);
let last_few_twevent: Vec<Infos> = tweeter.display_info.infos[last_twevent..].iter().map(|x| x.clone()).rev().collect::<Vec<Infos>>();
let mut h = 7;
for info in last_few_twevent {
- let mut to_draw = match info {
+ let to_draw: Vec<String> = match info {
Infos::Tweet(id) => {
- render_twete(&id, tweeter).iter().map(|x| x.to_owned()).rev().collect()
+ let pre_split: Vec<String> = render_twete(&id, tweeter);
+ let split_on_newline: Vec<String> = pre_split.into_iter().flat_map(|x| x.split("\n").map(|x| x.to_owned()).collect::<Vec<String>>()).collect();
+ let wrapped: Vec<String> = split_on_newline.iter()
+ .map(|x| x.chars().collect::<Vec<char>>())
+ .flat_map(|x| x.chunks(_width as usize)
+ .map(|x| x.into_iter().collect::<String>())
+ .collect::<Vec<String>>())
+ .collect();
+ wrapped.into_iter().rev().collect()
+ }
+ Infos::TweetWithContext(id, context) => {
+ let mut lines = render_twete(&id, tweeter).iter().map(|x| x.to_owned()).rev().collect::<Vec<String>>();
+ lines.push(context);
+ lines
}
- Infos::Thread(ids) => {
- vec![format!("{}{}I'd show a thread if I knew how", cursor::Goto(1, height - h), clear::CurrentLine)]
+ Infos::Thread(_ids) => {
+ let mut lines = vec![format!("{}{}I'd show a thread if I knew how", cursor::Goto(1, height - h), clear::CurrentLine)];
+ lines.push("".to_owned());
+// lines.push(format!("link: https://twitter.com/i/web/status/{}", id));
+ lines
},
Infos::Event(e) => {
e.clone().render(tweeter).into_iter().rev().collect()
@@ -61,35 +107,39 @@ pub fn paint(tweeter: &mut ::tw::TwitterCache) {
Infos::DM(msg) => {
vec![format!("{}{}DM: {}", cursor::Goto(1, height - h), clear::CurrentLine, msg)]
}
+ Infos::User(user) => {
+ vec![
+ format!("{} (@{})", user.name, user.handle)
+ ]
+ }
};
for line in to_draw {
- println!("{}{}{}", cursor::Goto(1, height - h), clear::CurrentLine, line);
+ print!("{}{}{}", cursor::Goto(1, height - h), clear::CurrentLine, line);
h = h + 1;
if h >= height {
print!("{}", cursor::Goto(2, height - 6));
- stdout().flush();
- return;
+ return stdout().flush();
}
}
- println!("{}{}", cursor::Goto(1, height - h), clear::CurrentLine);
+ print!("{}{}", cursor::Goto(1, height - h), clear::CurrentLine);
h = h + 1;
if h >= height {
print!("{}", cursor::Goto(2, height - 6));
- stdout().flush();
- return;
+ return stdout().flush();
}
}
while h < height {
- println!("{}{}", cursor::Goto(1, height - h), clear::CurrentLine);
+ print!("{}{}", cursor::Goto(1, height - h), clear::CurrentLine);
h = h + 1;
}
- print!("{}", cursor::Goto(2, height - 6));
- stdout().flush();
+ print!("{}", cursor::Goto(2, height - 5));
+ stdout().flush()?;
},
Err(e) => {
println!("Can't get term dimensions: {}", e);
}
}
+ Ok(())
}
fn color_for(handle: &String) -> termion::color::Fg<&color::Color> {
@@ -125,7 +175,7 @@ impl Render for tw::events::Event {
result.push("---------------------------------".to_string());
{
let user = tweeter.retrieve_user(&user_id).unwrap();
- println!(" quoted_tweet : {} (@{})", user.name, user.handle);
+ result.push(format!(" quoted_tweet : {} (@{})", user.name, user.handle));
}
render_twete(&TweetId::Twitter(twete_id), tweeter);
}
diff --git a/src/main.rs b/src/main.rs
index 4352fe9..c46fec1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -55,10 +55,10 @@ pub struct Queryer {
}
impl Queryer {
- fn do_api_get(&mut self, url: &str) -> Option<serde_json::Value> {
+ fn do_api_get(&mut self, url: &str) -> Result<serde_json::Value, String> {
self.issue_request(signed_api_get(url))
}
- fn do_api_post(&mut self, url: &str) -> Option<serde_json::Value> {
+ fn do_api_post(&mut self, url: &str) -> Result<serde_json::Value, String> {
self.issue_request(signed_api_post(url))
}
/*
@@ -66,7 +66,7 @@ impl Queryer {
self.issue_request(signed_web_get(url))
}*/
// TODO: make this return the status as well!
- fn issue_request(&mut self, req: hyper::client::Request) -> Option<serde_json::Value> {
+ fn issue_request(&mut self, req: hyper::client::Request) -> Result<serde_json::Value, String> {
let lookup = self.client.request(req);
let resp: hyper::Response = self.core.run(lookup).unwrap();
@@ -79,19 +79,13 @@ impl Queryer {
match serde_json::from_slice(&resp_body) {
Ok(value) => {
if status != hyper::StatusCode::Ok {
- println!("!! Requests returned status: {}", status);
- println!("{}", value);
- None
+ Err(format!("!! Requests returned status: {}\n{}", status, value))
} else {
- Some(value)
+ Ok(value)
}
}
Err(e) => {
- if status != hyper::StatusCode::Ok {
- println!("!! Requests returned status: {}", status);
- }
- println!("error deserializing json: {}", e);
- None
+ Err(format!("!! Requests returned status: {}\nerror deserializing json: {}", status, e))
}
}
}
@@ -171,7 +165,6 @@ fn signed_api_req(url: &str, method: Method) -> hyper::client::Request {
headers.set(Accept("*/*".to_owned()));
};
-// println!("Request built: {:?}", req);
req
}
@@ -181,8 +174,6 @@ fn main() {
// let url = "https://stream.twitter.com/1.1/statuses/filter.json";
// let url = "https://stream.twitter.com/1.1/statuses/sample.json";
- println!("starting!");
-
let (ui_tx, mut ui_rx) = chan::sync::<Vec<u8>>(0);
let mut twete_rx = connect_twitter_stream();
@@ -231,8 +222,6 @@ fn main() {
}
}
}
-
- println!("Bye bye");
}
fn do_ui(ui_rx_orig: chan::Receiver<Vec<u8>>, twete_rx: chan::Receiver<Vec<u8>>, mut tweeter: &mut tw::TwitterCache, mut queryer: &mut ::Queryer) -> Option<(chan::Receiver<Vec<u8>>, chan::Receiver<Vec<u8>>)> {
@@ -243,7 +232,6 @@ fn do_ui(ui_rx_orig: chan::Receiver<Vec<u8>>, twete_rx: chan::Receiver<Vec<u8>>,
twete_rx.recv() -> twete => match twete {
Some(line) => {
let jsonstr = std::str::from_utf8(&line).unwrap().trim();
-// println!("{}", jsonstr);
/* TODO: replace from_str with from_slice */
let json: serde_json::Value = serde_json::from_str(&jsonstr).unwrap();
tw::handle_message(json, &mut tweeter, &mut queryer);
@@ -252,7 +240,7 @@ fn do_ui(ui_rx_orig: chan::Receiver<Vec<u8>>, twete_rx: chan::Receiver<Vec<u8>>,
}
}
None => {
- println!("Twitter stream hung up...");
+ tweeter.display_info.status("Twitter stream hung up...".to_owned());
chan_select! {
ui_rx_b.recv() -> input => match input {
Some(line) => {
@@ -271,13 +259,16 @@ fn do_ui(ui_rx_orig: chan::Receiver<Vec<u8>>, twete_rx: chan::Receiver<Vec<u8>>,
Some(line) => {
tweeter.handle_user_input(line, &mut queryer);
},
- None => println!("UI thread hung up...")
+ None => tweeter.display_info.status("UI thread hung up...".to_owned())
}
// 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);
+ match display::paint(tweeter) {
+ Ok(_) => (),
+ Err(e) => println!("{}", e) // TODO: we got here because writing to stdout failed. what to do now?
+ };
}
}
@@ -326,9 +317,6 @@ fn connect_twitter_stream() -> chan::Receiver<Vec<u8>> {
.connector(connector)
.build(&core.handle());
- // println!("{}", do_web_req("https://caps.twitter.com/v2/capi/passthrough/1?twitter:string:card_uri=card://887655800482787328&twitter:long:original_tweet_id=887655800981925888&twitter:string:response_card_name=poll3choice_text_only&twitter:string:cards_platform=Web-12", &client, &mut core).unwrap());
- // println!("{}", look_up_tweet("887655800981925888", &client, &mut core).unwrap());
-
let req = signed_api_get(STREAMURL);
let work = client.request(req).and_then(|res| {
let status = res.status();
diff --git a/src/tw/events.rs b/src/tw/events.rs
index 35167a3..0da27a1 100644
--- a/src/tw/events.rs
+++ b/src/tw/events.rs
@@ -22,11 +22,13 @@ impl Event {
fn get_source_target_ids(structure: serde_json::Map<String, serde_json::Value>) -> Result<(String, String), String> {
match (
structure.get("source").and_then(|x| x.get("id_str").and_then(|x| x.as_str())),
- structure.get("target_obj").and_then(|x| x.get("id_str").and_then(|x| x.as_str()))
+ structure.get("target_object").and_then(|x| x.get("id_str").and_then(|x| x.as_str()))
) {
(Some(source_id), Some(target_id)) => Ok((source_id.to_string(), target_id.to_string())),
- (None, Some(target_id)) => Err("No id_str string at .source.id_str".to_string()),
- (Some(target_id), None) => Err("No id_str string at .target_object.id_str".to_string()),
+ // have more particular error types for "missing fields", "missing data", "invalid
+ // state", etc, so downstream we can opt to investigate the bad data or not..
+ (None, Some(_)) => Err("No id_str string at .source.id_str: {}".to_string()),
+ (Some(_), None) => Err("No id_str string at .target_object.id_str: {}".to_string()),
(None, None) => Err("No id_str at source or target_object".to_string())
}
}
@@ -96,7 +98,7 @@ impl Event {
// what about removed?
// "blocked" => Blocked { },
// "unblocked" => Unblocked { },
- e => { println!("unrecognized event: {}", e); Err(e.to_string()) }
+ e => Err(e.to_string())
}
},
None => {
diff --git a/src/tw/mod.rs b/src/tw/mod.rs
index d0bad59..82dfe10 100644
--- a/src/tw/mod.rs
+++ b/src/tw/mod.rs
@@ -15,7 +15,6 @@ use std::fs::OpenOptions;
pub mod events;
-use display::Render;
use display;
pub mod tweet;
@@ -104,7 +103,7 @@ pub struct TwitterCache {
#[serde(skip)]
id_conversions: IdConversions,
#[serde(skip)]
- pub display_info: DisplayInfo
+ pub display_info: display::DisplayInfo
}
// Internally, a monotonically increasin i64 is always the id used.
@@ -217,30 +216,6 @@ 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 {
- pub 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;
@@ -284,7 +259,7 @@ impl TwitterCache {
current_user: User::default(),
threads: HashMap::new(),
id_conversions: IdConversions::default(),
- display_info: DisplayInfo::default()
+ display_info: display::DisplayInfo::default()
}
}
@@ -298,7 +273,6 @@ impl TwitterCache {
} 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 {
@@ -332,7 +306,7 @@ impl TwitterCache {
self.number_and_insert_tweet(tweet);
}
}
- pub fn store_cache(&self) {
+ pub fn store_cache(&mut self) {
if Path::new(TwitterCache::PROFILE_DIR).is_dir() {
let profile = OpenOptions::new()
.write(true)
@@ -342,7 +316,7 @@ impl TwitterCache {
.unwrap();
serde_json::to_writer(profile, self).unwrap();
} else {
- println!("No cache dir exists...");
+ self.display_info.status("No cache dir exists...".to_owned());
}
// store cache
}
@@ -398,23 +372,29 @@ impl TwitterCache {
}
}
pub fn cache_api_tweet(&mut self, json: serde_json::Value) {
- if let Some((rt, rt_user)) = json.get("retweeted_status").and_then(|x| Tweet::from_api_json(x.to_owned())) {
+ // TODO: log error somehow
+ if let Some(Ok((rt, rt_user))) = json.get("retweeted_status").map(|x| Tweet::from_api_json(x.to_owned())) {
self.cache_user(rt_user);
self.cache_tweet(rt);
}
- if let Some((qt, qt_user)) = json.get("quoted_status").and_then(|x| Tweet::from_api_json(x.to_owned())) {
+ // TODO: log error somehow
+ if let Some(Ok((qt, qt_user))) = json.get("quoted_status").map(|x| Tweet::from_api_json(x.to_owned())) {
self.cache_user(qt_user);
self.cache_tweet(qt);
}
- if let Some((twete, user)) = Tweet::from_api_json(json) {
+ // TODO: log error somehow
+ if let Ok((twete, user)) = Tweet::from_api_json(json) {
self.cache_user(user);
self.cache_tweet(twete);
}
}
pub fn cache_api_user(&mut self, json: serde_json::Value) {
- if let Some(user) = User::from_json(json) {
+ // TODO: log error somehow
+ // TODO: probably means display_info needs a more technical-filled log for debugging,
+ // independent of the user-facing statuses, like "invalid id"
+ if let Ok(user) = User::from_json(json) {
self.cache_user(user);
}
}
@@ -517,8 +497,8 @@ impl TwitterCache {
&TweetId::Twitter(ref id) => {
if !self.tweets.contains_key(id) {
match self.look_up_tweet(id, &mut queryer) {
- Some(json) => self.cache_api_tweet(json),
- None => self.display_info.status(format!("Unable to retrieve tweet {}", id))
+ Ok(json) => self.cache_api_tweet(json),
+ Err(e) => self.display_info.status(format!("Unable to retrieve tweet {}:\n{}", id, e))
};
}
self.retrieve_tweet(tweet_id)
@@ -529,8 +509,8 @@ impl TwitterCache {
if !self.users.contains_key(user_id) {
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 => self.display_info.status(format!("Unable to retrieve user {}", user_id))
+ Ok(tw) => self.cache_user(tw),
+ Err(e) => self.display_info.status(format!("Unable to retrieve user {}:\n{}", user_id, e))
}
}
self.users.get(user_id)
@@ -540,13 +520,13 @@ impl TwitterCache {
let new_uids = &uid_set - &self.following;
for user in &new_uids {
- println!("New following! {}", user);
+ self.display_info.status(format!("New following! {}", user));
self.add_following(user);
}
let lost_uids = &self.following - &uid_set;
for user in &lost_uids {
- println!("Bye, friend! {}", user);
+ self.display_info.status(format!("Bye, friend! {}", user));
self.remove_following(user);
}
}
@@ -555,13 +535,13 @@ impl TwitterCache {
let new_uids = &uid_set - &self.followers;
for user in &new_uids {
- println!("New follower! {}", user);
+ self.display_info.status(format!("New follower! {}", user));
self.add_follower(user);
}
let lost_uids = &self.followers - &uid_set;
for user in &lost_uids {
- println!("Bye, friend! {}", user);
+ self.display_info.status(format!("Bye, friend! {}", user));
self.remove_follower(user);
}
}
@@ -588,21 +568,21 @@ impl TwitterCache {
self.follower_history.insert(user_id.to_owned(), ("unfollow".to_string(), Utc::now().timestamp()));
}
- fn look_up_user(&mut self, id: &str, queryer: &mut ::Queryer) -> Option<serde_json::Value> {
+ fn look_up_user(&mut self, id: &str, queryer: &mut ::Queryer) -> Result<serde_json::Value, String> {
let url = &format!("{}?user_id={}", ::USER_LOOKUP_URL, id);
queryer.do_api_get(url)
}
- fn look_up_tweet(&mut self, id: &str, queryer: &mut ::Queryer) -> Option<serde_json::Value> {
+ fn look_up_tweet(&mut self, id: &str, queryer: &mut ::Queryer) -> Result<serde_json::Value, String> {
let url = &format!("{}&id={}", ::TWEET_LOOKUP_URL, id);
queryer.do_api_get(url)
}
- pub fn get_settings(&self, queryer: &mut ::Queryer) -> Option<serde_json::Value> {
+ pub fn get_settings(&self, queryer: &mut ::Queryer) -> Result<serde_json::Value, String> {
queryer.do_api_get(::ACCOUNT_SETTINGS_URL)
}
- pub fn get_followers(&self, queryer: &mut ::Queryer) -> Option<serde_json::Value> {
+ pub fn get_followers(&self, queryer: &mut ::Queryer) -> Result<serde_json::Value, String> {
queryer.do_api_get(::GET_FOLLOWER_IDS_URL)
}
@@ -684,7 +664,6 @@ fn handle_twitter_welcome(
structure: serde_json::Map<String, serde_json::Value>,
tweeter: &mut TwitterCache,
queryer: &mut ::Queryer) {
-// println!("welcome: {:?}", structure);
let user_id_nums = structure["friends"].as_array().unwrap();
let user_id_strs = user_id_nums.into_iter().map(|x| x.as_u64().unwrap().to_string());
tweeter.set_following(user_id_strs.collect());
@@ -722,6 +701,8 @@ pub fn handle_message(
handle_twitter_twete(objmap, tweeter, queryer);
} else if objmap.contains_key("direct_message") {
handle_twitter_dm(objmap, tweeter, queryer);
+ } else {
+ tweeter.display_info.status(format!("Unknown json: {:?}", objmap));
}
// self.display_info.status("");
},
diff --git a/src/tw/tweet.rs b/src/tw/tweet.rs
index a3fdde3..dc89774 100644
--- a/src/tw/tweet.rs
+++ b/src/tw/tweet.rs
@@ -38,14 +38,17 @@ impl Tweet {
.collect()
}
- pub fn from_api_json(json: serde_json::Value) -> Option<(Tweet, User)> {
+ pub fn from_api_json(json: serde_json::Value) -> Result<(Tweet, User), String> {
Tweet::from_json(json.clone()).and_then(|tw| {
- json.get("user").and_then(|user_json|
- User::from_json(user_json.to_owned()).map(|u| (tw, u))
- )
+ match json.get("user") {
+ Some(user_json) =>
+ User::from_json(user_json.to_owned()).map(|u| (tw, u)),
+ None =>
+ Err("No user json".to_owned())
+ }
})
}
- pub fn from_json(json: serde_json::Value) -> Option<Tweet> {
+ pub fn from_json(json: serde_json::Value) -> Result<Tweet, String> {
if let serde_json::Value::Object(json_map) = json {
let text = ::tw::full_twete_text(&json_map);
let rt_twete = json_map.get("retweeted_status")
@@ -67,7 +70,7 @@ impl Tweet {
json_map["user"]["id_str"].as_str(),
json_map["created_at"].as_str()
) {
- return Some(Tweet {
+ return Ok(Tweet {
id: id_str.to_owned(),
author_id: author_id.to_owned(),
text: text,
@@ -82,6 +85,6 @@ impl Tweet {
}
}
}
- None
+ Err("Invalid tweet json".to_owned())
}
}
diff --git a/src/tw/user.rs b/src/tw/user.rs
index 8f41b6d..0af4eb8 100644
--- a/src/tw/user.rs
+++ b/src/tw/user.rs
@@ -18,29 +18,28 @@ impl Default for User {
}
impl User {
- pub fn from_json(json: serde_json::Value) -> Option<User> {
+ pub fn from_json(json: serde_json::Value) -> Result<User, String> {
if let serde_json::Value::Object(json_map) = json {
- if json_map.contains_key("id_str") &&
- json_map.contains_key("name") &&
- json_map.contains_key("screen_name") {
- if let (
- Some(id_str),
- Some(name),
- Some(screen_name)
- ) = (
- json_map["id_str"].as_str(),
- json_map["name"].as_str(),
- json_map["screen_name"].as_str()
- ) {
- return Some(User {
- id: id_str.to_owned(),
- name: name.to_owned(),
- handle: screen_name.to_owned()
- })
- }
+ if let (
+ Some(id_str),
+ Some(name),
+ Some(screen_name)
+ ) = (
+ json_map.get("id_str").and_then(|x| x.as_str()),
+ json_map.get("name").and_then(|x| x.as_str()),
+ json_map.get("screen_name").and_then(|x| x.as_str())
+ ) {
+ Ok(User {
+ id: id_str.to_owned(),
+ name: name.to_owned(),
+ handle: screen_name.to_owned()
+ })
+ } else {
+ Err("user json missing one of id_str, name, screen_name".to_owned())
}
+ } else {
+ Err(format!("Invalid json: {:?}", json))
}
- None
}
}