pub mod config_chain;
pub mod model_resolve;
pub mod paths;
pub mod plugin_protocol;
pub mod plugin_service;
pub mod project;
pub mod protocol;
pub mod subscription_usage;
pub mod tool_prompt;
pub mod types;
pub mod usage_totals;
pub use types::*;
pub use usage_totals::UsageTotals;
pub fn write_json_line<T: serde::Serialize>(
writer: &mut impl std::io::Write,
val: &T,
) -> Result<()> {
let mut line = serde_json::to_string(val).map_err(|e| Error::Io(e.to_string()))?;
line.push('\n');
writer
.write_all(line.as_bytes())
.map_err(|e| Error::Io(e.to_string()))?;
writer.flush().map_err(|e| Error::Io(e.to_string()))?;
Ok(())
}
pub fn read_json_line<T: serde::de::DeserializeOwned>(
reader: &mut impl std::io::BufRead,
) -> Result<Option<T>> {
let mut line = String::new();
let n = reader
.read_line(&mut line)
.map_err(|e| Error::Io(e.to_string()))?;
if n == 0 {
return Ok(None);
}
let val = serde_json::from_str(&line).map_err(|e| Error::Parse(e.to_string()))?;
Ok(Some(val))
}
pub async fn write_json_line_async<T: serde::Serialize>(
writer: &mut (impl futures::io::AsyncWrite + Unpin),
val: &T,
) -> Result<()> {
use futures::io::AsyncWriteExt;
let mut line = serde_json::to_string(val).map_err(|e| Error::Io(e.to_string()))?;
line.push('\n');
writer
.write_all(line.as_bytes())
.await
.map_err(|e| Error::Io(e.to_string()))?;
writer.flush().await.map_err(|e| Error::Io(e.to_string()))?;
Ok(())
}
pub async fn read_json_line_async<T: serde::de::DeserializeOwned>(
reader: &mut (impl futures::io::AsyncBufRead + Unpin),
) -> Result<Option<T>> {
use futures::io::AsyncBufReadExt;
let mut line = String::new();
let n = reader
.read_line(&mut line)
.await
.map_err(|e| Error::Io(e.to_string()))?;
if n == 0 {
return Ok(None);
}
let val = serde_json::from_str(&line).map_err(|e| Error::Parse(e.to_string()))?;
Ok(Some(val))
}
pub fn truncate_str(s: &str, max_bytes: usize) -> &str {
if s.len() <= max_bytes {
return s;
}
let mut end = max_bytes;
while end > 0 && !s.is_char_boundary(end) {
end -= 1;
}
&s[..end]
}
pub fn truncate_str_end(s: &str, max_bytes: usize) -> &str {
if s.len() <= max_bytes {
return s;
}
let mut start = s.len() - max_bytes;
while start < s.len() && !s.is_char_boundary(start) {
start += 1;
}
&s[start..]
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("no provider registered for api: {0}")]
NoProvider(String),
#[error("no API key for provider: {0}")]
NoApiKey(String),
#[error("HTTP error: {0}")]
Http(String),
#[error("HTTP {status}: {message}")]
HttpStatus {
status: u16,
message: String,
retry_after: Option<u64>,
},
#[error("parse error: {0}")]
Parse(String),
#[error("IO error: {0}")]
Io(String),
#[error("channel closed")]
ChannelClosed,
#[error("cancelled")]
Cancelled,
#[error("provider throttled until {0}")]
Throttled(String),
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
pub(crate) static TEST_ENV_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());