use object_store::path::DELIMITER;
use std::{future::Future, pin::Pin};
pub mod agent;
pub mod context;
pub mod http;
pub mod json;
pub mod model;
pub mod tool;
pub use agent::*;
pub use context::*;
pub use http::*;
pub use json::*;
pub use model::*;
pub use tool::*;
pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
pub type BoxPinFut<T> = Pin<Box<dyn Future<Output = T> + Send>>;
pub fn path_lowercase(path: &Path) -> Path {
let mut path = path.to_string();
path.make_ascii_lowercase();
path.into()
}
pub fn path_join(a: &Path, b: &Path) -> Path {
let mut path = if a.is_root() {
b.to_string()
} else if b.is_root() {
a.to_string()
} else {
format!("{}{}{}", a, DELIMITER, b)
};
path.make_ascii_lowercase();
path.into()
}
pub fn validate_path_part(part: &str) -> Result<(), BoxError> {
if part.is_empty() || part.contains(DELIMITER) || Path::from(part).as_ref() != part {
return Err(format!("invalid path part: {}", part).into());
}
Ok(())
}
pub fn validate_function_name(name: &str) -> Result<(), BoxError> {
if name.is_empty() {
return Err("empty string".into());
}
if name.len() > 64 {
return Err("string length exceeds the limit 64".into());
}
let mut iter = name.chars();
if !matches!(iter.next(), Some('a'..='z')) {
return Err("name must start with a lowercase letter".into());
}
for c in iter {
if !matches!(c, 'a'..='z' | '0'..='9' | '_' ) {
return Err(format!("invalid character: {}", c).into());
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_lowercase() {
let a = Path::from("a/Foo");
assert_eq!(path_lowercase(&a).as_ref(), "a/foo");
}
#[test]
fn test_validate_path_part() {
assert!(validate_path_part("foo").is_ok());
assert!(validate_path_part("fOO").is_ok());
assert!(validate_path_part("").is_err());
assert!(validate_path_part("foo/").is_err());
assert!(validate_path_part("/foo").is_err());
assert!(validate_path_part("foo/bar").is_err());
assert!(validate_path_part("foo/bar/").is_err());
}
}