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.

125 lines
3.3 KiB

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