Skip to main content

hojicha_core/async_helpers/
mod.rs

1//! High-level async helper commands for common operations
2//!
3//! This module provides ergonomic helper functions for common async operations
4//! like HTTP requests, WebSocket connections, and file I/O.
5//!
6//! ## Available Helpers
7//!
8//! ### HTTP Operations
9//! Simple HTTP requests with automatic JSON handling:
10//! ```no_run
11//! # use hojicha_core::async_helpers::{http_get, http_post, HttpResponse, HttpError};
12//! # use hojicha_core::Cmd;
13//! # enum Msg { DataLoaded(String), Error(String), UserCreated(String) }
14//! // GET request
15//! let cmd: Cmd<Msg> = http_get("https://api.example.com/data", |result| {
16//!     match result {
17//!         Ok(response) => Msg::DataLoaded(response.body),
18//!         Err(err) => Msg::Error(err.to_string()),
19//!     }
20//! });
21//!
22//! // POST with JSON body (as string)
23//! let json_body = r#"{"name": "Alice"}"#;
24//! let cmd = http_post(
25//!     "https://api.example.com/users",
26//!     json_body,
27//!     |result| match result {
28//!         Ok(response) => Msg::UserCreated(response.body),
29//!         Err(e) => Msg::Error(e.to_string()),
30//!     }
31//! );
32//! ```
33//!
34//! ### WebSocket Connections
35//! Real-time bidirectional communication:
36//! ```no_run
37//! # use hojicha_core::async_helpers::{websocket, WebSocketEvent};
38//! # use hojicha_core::Cmd;
39//! # enum Msg { WsConnected, WsMessage(String), WsError(String), WsDisconnected, WsBinary(Vec<u8>) }
40//! let cmd: Cmd<Msg> = websocket("wss://echo.websocket.org", |event| {
41//!     Some(match event {
42//!         WebSocketEvent::Connected => Msg::WsConnected,
43//!         WebSocketEvent::Message(text) => Msg::WsMessage(text),
44//!         WebSocketEvent::Binary(data) => Msg::WsBinary(data),
45//!         WebSocketEvent::Error(err) => Msg::WsError(err.to_string()),
46//!         WebSocketEvent::Closed(_) => Msg::WsDisconnected,
47//!     })
48//! });
49//! ```
50//!
51//! ### File Operations
52//! Async file I/O and watching:
53//! ```no_run
54//! # use hojicha_core::async_helpers::{read_file, write_file, watch_file};
55//! # use hojicha_core::Cmd;
56//! # enum Msg { ConfigLoaded(String), Error(String), FileChanged }
57//! // Read file
58//! let cmd: Cmd<Msg> = read_file("config.json", |result| {
59//!     result.map(Msg::ConfigLoaded)
60//!           .unwrap_or_else(|e| Msg::Error(e.to_string()))
61//! });
62//!
63//! // Watch for changes
64//! let cmd = watch_file("data.csv", |_| Some(Msg::FileChanged));
65//! ```
66//!
67//! ### Timers
68//! Delays and intervals:
69//! ```no_run
70//! # use hojicha_core::async_helpers::{delay, interval};
71//! # use hojicha_core::Cmd;
72//! # use std::time::Duration;
73//! # enum Msg { TimerExpired, Tick(usize) }
74//! // One-shot delay
75//! let cmd: Cmd<Msg> = delay(Duration::from_secs(2), || Msg::TimerExpired);
76//!
77//! // Repeating interval
78//! let cmd = interval(Duration::from_secs(1), |count| Msg::Tick(count));
79//! ```
80
81pub mod file_io;
82pub mod http;
83pub mod timer;
84pub mod websocket;
85
86pub use file_io::{read_file, watch_file, write_file, FileError, FileEvent};
87pub use http::{http_get, http_post, http_request, HttpError, HttpMethod, HttpResponse};
88pub use timer::{debounce, delay, interval, throttle, with_timeout};
89pub use websocket::{websocket, WebSocketError, WebSocketEvent};
90
91/// Result type for async operations
92pub type AsyncResult<T> = Result<T, Box<dyn std::error::Error + Send + Sync>>;
93
94/// Common configuration for async operations
95#[derive(Debug, Clone)]
96pub struct AsyncConfig {
97    /// Timeout for operations
98    pub timeout: Option<std::time::Duration>,
99    /// Number of retries
100    pub retries: u32,
101    /// Backoff strategy for retries
102    pub backoff: BackoffStrategy,
103}
104
105/// Backoff strategy for retries
106#[derive(Debug, Clone)]
107pub enum BackoffStrategy {
108    /// No backoff
109    None,
110    /// Linear backoff (delay * attempt)
111    Linear(std::time::Duration),
112    /// Exponential backoff (delay * 2^attempt)
113    Exponential(std::time::Duration),
114}
115
116impl Default for AsyncConfig {
117    fn default() -> Self {
118        Self {
119            timeout: Some(std::time::Duration::from_secs(30)),
120            retries: 0,
121            backoff: BackoffStrategy::None,
122        }
123    }
124}
125
126impl AsyncConfig {
127    /// Create a config with a specific timeout
128    #[must_use]
129    pub fn with_timeout(mut self, timeout: std::time::Duration) -> Self {
130        self.timeout = Some(timeout);
131        self
132    }
133
134    /// Create a config with retries
135    #[must_use]
136    pub fn with_retries(mut self, retries: u32, backoff: BackoffStrategy) -> Self {
137        self.retries = retries;
138        self.backoff = backoff;
139        self
140    }
141}