Simple pomodoro timer for command line
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
3.1 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. use chrono::prelude::*;
  2. use chrono::Duration;
  3. use std::io::Error;
  4. use std::io::{stdout, Result, StdoutLock, Write};
  5. use std::process::{Command, Output};
  6. use std::thread::sleep;
  7. const TIME_FORMAT: &'static str = "%H:%M:%S";
  8. fn main() {
  9. run().unwrap();
  10. }
  11. /// Check if kdialog exists. If not, print message and return Err()
  12. fn check_deps() -> Result<()> {
  13. let cmd = Command::new("sh").arg("-c").arg("kdialog").status()?;
  14. if !cmd.success() {
  15. println!("Could not find command 'kdialog'. Please install first, then start again");
  16. return Err(Error::new(
  17. std::io::ErrorKind::Other,
  18. "Could not find kdialog",
  19. ));
  20. }
  21. return Ok(());
  22. }
  23. /// Runs the application
  24. ///
  25. /// Default time settings are 25 minutes for a working block and 5 minutes for a break.
  26. fn run() -> Result<()> {
  27. if check_deps().is_err() {
  28. // terminate gracefully
  29. return Ok(());
  30. }
  31. let work_duration = Duration::seconds(if cfg!(debug_assertions) { 75 } else { 25 * 60 });
  32. let break_duration = Duration::seconds(if cfg!(debug_assertions) { 5 } else { 5 * 60 });
  33. loop {
  34. timer(
  35. "Work",
  36. "The working time is over, have a break now!",
  37. &work_duration,
  38. )?;
  39. timer(
  40. "Break",
  41. "The break is over, get back to work!",
  42. &break_duration,
  43. )?;
  44. }
  45. }
  46. /// Handles a timer block
  47. ///
  48. /// # Arguments
  49. ///
  50. /// * name - The name for this block, e.g. 'Work' or 'Break'
  51. /// * after_msg - The message to be displayed by the Dialog window at the end of the time
  52. /// * duration - How long this block shall be
  53. fn timer(name: &str, after_msg: &str, duration: &Duration) -> Result<()> {
  54. let out = stdout();
  55. let mut out = out.lock();
  56. let start = Local::now();
  57. writeln!(out, "{} start {}", name, start.format(TIME_FORMAT))?;
  58. loop {
  59. reset_line(&mut out)?;
  60. let elapsed: Duration = Local::now().signed_duration_since(start);
  61. if elapsed >= *duration {
  62. break;
  63. }
  64. let elapsed_time: NaiveTime = NaiveTime::from_hms(
  65. (elapsed.num_hours() % 24) as u32,
  66. (elapsed.num_minutes() % 60) as u32,
  67. (elapsed.num_seconds() % 60) as u32,
  68. );
  69. write!(out, "Time elapsed: {}", elapsed_time.format(TIME_FORMAT))?;
  70. out.flush().unwrap();
  71. sleep(Duration::seconds(1).to_std().unwrap());
  72. }
  73. let now = Local::now();
  74. writeln!(out, "{} end {}", name, now.format(TIME_FORMAT))?;
  75. dialog(after_msg).unwrap();
  76. Ok(())
  77. }
  78. /// Shows a dialog
  79. ///
  80. /// Currently only supports KDE dialogs via command line program `kdialgo`.
  81. ///
  82. /// # Arguments
  83. ///
  84. /// * text - The text to be shown in the Dialog
  85. fn dialog(text: &str) -> Result<Output> {
  86. Command::new("sh")
  87. .arg("-c")
  88. .arg(format!(
  89. "kdialog --title 'Pomodoro Timer' --msgbox '{}'",
  90. text
  91. ))
  92. .output()
  93. }
  94. /// Resets the current line on stdout
  95. ///
  96. /// Works for me® but is probably unportable as hell.
  97. ///
  98. /// # Arguments
  99. ///
  100. /// * w - A reference to the locked stdout handle
  101. pub fn reset_line(w: &mut StdoutLock) -> Result<()> {
  102. write!(w, "\r[2K")
  103. }