DEV Community

Cover image for Clojure Is Awesome!!! [PART 23]
André Borba
André Borba

Posted on

Clojure Is Awesome!!! [PART 23]

But What If KISS? đź‘€

(ns clojure-is-awesome.kiss
  (:require [clojure.spec.alpha :as s]))

(s/def ::id string?)
(s/def ::type #{"work" "break"})
(s/def ::duration (s/and int? pos?))
(s/def ::start-time #(instance? java.time.Instant %))
(s/def ::end-time #(instance? java.time.Instant %))
(s/def ::status #{:running :completed :paused :cancelled})
(s/def ::session (s/keys :req-un [::id ::type ::duration ::start-time ::end-time]))
(s/def ::sessions (s/coll-of ::session :kind vector))

(defn create-session
  "Creates a Pomodoro session with computed end-time."
  [id type duration]
  (let [start-time (java.time.Instant/now)
        end-time (.plusSeconds start-time (* duration 60))
        session {:id id
                 :type type
                 :duration duration
                 :start-time start-time
                 :end-time end-time}]
    (if (s/valid? ::session session)
      session
      (throw (ex-info "Invalid session" {:errors (s/explain-data ::session session)})))))

(defn set-session-status
  "Sets a session’s status (e.g., paused, cancelled)."
  [session status]
  {:pre [(s/valid? ::status status)]}
  (assoc session :status status))

(defn session-status
  "Returns session status and remaining time as data, given current time."
  [session now]
  {:pre [(s/valid? ::session session)
         (instance? java.time.Instant now)]}
  (if (:status session)
    {:status (:status session) :remaining-minutes 0}
    (let [end-time (:end-time session)
          remaining-secs (max 0 (- (.getEpochSecond end-time) (.getEpochSecond now)))]
      {:status (if (pos? remaining-secs) :running :completed)
       :remaining-minutes (int (/ remaining-secs 60))})))

(defn format-session
  "Formats a session’s status for display."
  [session now]
  (let [{:keys [status remaining-minutes]} (session-status session now)]
    (str (:id session) ": " (:type session) " "
         (case status
           :running (str "running, " remaining-minutes " minutes remaining")
           :completed "completed"
           :paused "paused"
           :cancelled "cancelled"))))

(defn generate-report
  "Generates a status report for all sessions."
  [sessions now]
  {:pre [(s/valid? ::sessions sessions)
         (instance? java.time.Instant now)]}
  (str "Pomodoro Status Report\n"
       (apply str
              (map #(str (format-session % now) "\n") sessions))))

(comment
  (def sample-sessions
    [(create-session "s1" "work" 25)
     (create-session "s2" "break" 5)])

  (session-status (first sample-sessions) (java.time.Instant/now))
  ;; => {:status :running, :remaining-minutes 25} (or less, depending on timing)

  (generate-report sample-sessions (java.time.Instant/now))
  ;; => "Pomodoro Status Report\ns1: work running, 25 minutes remaining\ns2: break running, 5 minutes remaining"

  ;; Simulate paused and cancelled states
  (def paused-session (set-session-status (first sample-sessions) :paused))
  (session-status paused-session (java.time.Instant/now))
  ;; => {:status :paused, :remaining-minutes 0}

  ;; Simulate completed session (adjust start-time for testing)
  (def completed-session
    (assoc (second sample-sessions)
           :start-time (.minusSeconds (java.time.Instant/now) (* 6 60))))
  (session-status completed-session (java.time.Instant/now))
  ;; => {:status :completed, :remaining-minutes 0}
)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)