commit df562c64d4469a4f2cfd0dcc05859303f176625d
parent e406cbd47f5a8d3129b97df87f0bd3c8919d9a0f
Author: Shimmy Xu <shimmy.xu@shimmy1996.com>
Date: Sun, 15 Sep 2019 17:43:30 -0400
Add support for relative timestamps
Diffstat:
3 files changed, 68 insertions(+), 10 deletions(-)
diff --git a/config.example b/config.example
@@ -4,6 +4,8 @@ twtfile = ~/.local/share/twixter/twtxt.txt
twturl = https://example.org/twtxt.txt
pre_tweet_hook = "scp buckket@example.org:~/public_html/twtxt.txt {twtfile}"
post_tweet_hook = "scp {twtfile} buckket@example.org:~/public_html/twtxt.txt"
+limit_timeline = 20
+use_abs_time = false
[following]
bob = https://example.org/bob.txt
diff --git a/src/config.rs b/src/config.rs
@@ -9,6 +9,7 @@ pub struct Config {
pub pre_tweet_hook: String,
pub post_tweet_hook: String,
pub limit_timeline: i32,
+ pub use_abs_time: bool,
pub following: HashMap<String, String>,
}
@@ -35,6 +36,7 @@ impl Config {
pre_tweet_hook: pre_tweet_hook,
post_tweet_hook: post_tweet_hook,
limit_timeline: twtxt_config["limit_timeline"].parse::<i32>().unwrap(),
+ use_abs_time: twtxt_config["use_abs_time"].parse::<bool>().unwrap(),
following: following,
}
}
diff --git a/src/timeline.rs b/src/timeline.rs
@@ -1,4 +1,4 @@
-use chrono::{DateTime, FixedOffset, Local, SecondsFormat};
+use chrono::{DateTime, Duration, FixedOffset, Local, SecondsFormat};
use clap::ArgMatches;
use reqwest::Client;
@@ -27,7 +27,7 @@ pub fn timeline(config: &Config, _subcommand: &ArgMatches) {
let now = Local::now();
for _ in 0..config.limit_timeline {
if let Some(tweet) = all_tweets.pop() {
- println!("{}", format_tweet(&tweet, &now));
+ println!("{}", format_tweet(&tweet, &now, config.use_abs_time));
}
}
}
@@ -51,14 +51,68 @@ fn parse_twtxt(twturl: &str) -> Vec<(DateTime<FixedOffset>, String)> {
}
/// Formats a tweet for display in terminal.
-fn format_tweet(tweet: &(DateTime<FixedOffset>, String, String), now: &DateTime<Local>) -> String {
- format!(
- "\n@{} {}\n{}",
- &tweet.1,
- &tweet
+fn format_tweet(
+ tweet: &(DateTime<FixedOffset>, String, String),
+ now: &DateTime<Local>,
+ use_abs_time: bool,
+) -> String {
+ let timestamp = if use_abs_time {
+ tweet
.0
.with_timezone(&now.timezone())
- .to_rfc3339_opts(SecondsFormat::Secs, true),
- &tweet.2
- )
+ .to_rfc3339_opts(SecondsFormat::Secs, true)
+ } else {
+ format_duration(*now - tweet.0.with_timezone(&now.timezone()))
+ };
+
+ format!("\n@{} {}\n{}", &tweet.1, ×tamp, &tweet.2)
+}
+
+/// Formats a time duration in human readable format.
+/// Shows the first non-zero amount of year, month, week, day, hour, or
+/// minute. For duration shorter than one minute, return "just now".
+fn format_duration(duration: Duration) -> String {
+ let (num, unit) = if duration.num_days() >= 365 {
+ (duration.num_days() / 365, "year")
+ } else if duration.num_days() >= 30 {
+ (duration.num_days() / 30, "month")
+ } else if duration.num_weeks() >= 1 {
+ (duration.num_weeks(), "week")
+ } else if duration.num_days() >= 1 {
+ (duration.num_days(), "day")
+ } else if duration.num_hours() >= 1 {
+ (duration.num_hours(), "hour")
+ } else if duration.num_minutes() >= 1 {
+ (duration.num_minutes(), "minute")
+ } else {
+ return "just now".to_string();
+ };
+
+ if num > 1 {
+ format!("{} {}s ago", num, unit)
+ } else {
+ format!("{} {} ago", num, unit)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_format_duration() {
+ assert_eq!(format_duration(Duration::days(365 * 2)), "2 years ago");
+ assert_eq!(format_duration(Duration::days(365)), "1 year ago");
+ assert_eq!(format_duration(Duration::days(30 * 3)), "3 months ago");
+ assert_eq!(format_duration(Duration::days(30)), "1 month ago");
+ assert_eq!(format_duration(Duration::weeks(4)), "4 weeks ago");
+ assert_eq!(format_duration(Duration::weeks(1)), "1 week ago");
+ assert_eq!(format_duration(Duration::days(4)), "4 days ago");
+ assert_eq!(format_duration(Duration::days(1)), "1 day ago");
+ assert_eq!(format_duration(Duration::hours(23)), "23 hours ago");
+ assert_eq!(format_duration(Duration::hours(1)), "1 hour ago");
+ assert_eq!(format_duration(Duration::minutes(5)), "5 minutes ago");
+ assert_eq!(format_duration(Duration::minutes(1)), "1 minute ago");
+ assert_eq!(format_duration(Duration::seconds(30)), "just now");
+ }
}