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}