(ns examples.http "HTTP request example - demonstrates async commands. Mirrors bubbletea's http example." (:require [tui.core :as tui] [clojure.java.io :as io]) (:import [java.net URL HttpURLConnection])) ;; === HTTP Helpers === (defn http-get "Simple HTTP GET request. Returns {:status code :body string} or {:error msg}" [url-str] (try (let [url (URL. url-str) conn ^HttpURLConnection (.openConnection url)] (.setRequestMethod conn "GET") (.setConnectTimeout conn 5000) (.setReadTimeout conn 5000) (let [status (.getResponseCode conn) body (with-open [r (io/reader (.getInputStream conn))] (slurp r))] {:status status :body body})) (catch Exception e {:error (.getMessage e)}))) ;; === Model === (def initial-model {:state :idle ; :idle, :loading, :success, :error :status nil :error nil :url "https://httpstat.us/200"}) ;; === Commands === (defn fetch-url [url] (fn [] (let [result (http-get url)] (if (:error result) [:http-error (:error result)] [:http-success (:status result)])))) ;; === Update === (defn update-model [{:keys [url] :as model} msg] (cond ;; Quit (or (tui/key= msg "q") (tui/key= msg [:ctrl \c])) [model tui/quit] ;; Enter - start request (and (= (:state model) :idle) (tui/key= msg :enter)) [(assoc model :state :loading) (fetch-url url)] ;; r - retry/reset (tui/key= msg "r") [(assoc model :state :idle :status nil :error nil) nil] ;; HTTP success (= (first msg) :http-success) [(assoc model :state :success :status (second msg)) nil] ;; HTTP error (= (first msg) :http-error) [(assoc model :state :error :error (second msg)) nil] :else [model nil])) ;; === View === (defn view [{:keys [state status error url]}] [:col {:gap 1} [:box {:border :rounded :padding [1 2]} [:col {:gap 1} [:text {:bold true} "HTTP Request Demo"] [:text ""] [:row {:gap 1} [:text {:fg :gray} "URL:"] [:text {:fg :cyan} url]] [:text ""] (case state :idle [:text {:fg :gray} "Press enter to fetch..."] :loading [:row {:gap 1} [:text {:fg :yellow} "⠋"] [:text "Fetching..."]] :success [:row {:gap 1} [:text {:fg :green} "✓"] [:text (str "Status: " status)]] :error [:col [:row {:gap 1} [:text {:fg :red} "✗"] [:text {:fg :red} "Error:"]] [:text {:fg :red} error]])]] [:text {:fg :gray} (if (= state :idle) "enter: fetch q: quit" "r: retry q: quit")]]) ;; === Main === (defn -main [& _args] (println "Starting HTTP demo...") (let [final (tui/run {:init initial-model :update update-model :view view})] (when (= (:state final) :success) (println "Request completed with status:" (:status final)))))