Skip to main content

anda_core/
lib.rs

1use object_store::path::DELIMITER;
2use std::{future::Future, pin::Pin};
3
4pub mod agent;
5pub mod context;
6pub mod http;
7pub mod json;
8pub mod model;
9pub mod tool;
10
11pub use agent::*;
12pub use context::*;
13pub use http::*;
14pub use json::*;
15pub use model::*;
16pub use tool::*;
17
18/// A type alias for a boxed error that is thread-safe and sendable across threads.
19/// This is commonly used as a return type for functions that can return various error types.
20pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
21
22/// A type alias for a boxed future that is thread-safe and sendable across threads.
23pub type BoxPinFut<T> = Pin<Box<dyn Future<Output = T> + Send>>;
24
25/// Converts a path to lowercase path.
26pub fn path_lowercase(path: &Path) -> Path {
27    Path::from(path.as_ref().to_ascii_lowercase())
28}
29
30/// Validates a path part to ensure it doesn't contain the path delimiter
31/// agent name and user name should be validated.
32pub fn validate_path_part(part: &str) -> Result<(), BoxError> {
33    if part.is_empty() || part.contains(DELIMITER) || Path::from(part).as_ref() != part {
34        return Err(format!("invalid path part: {}", part).into());
35    }
36
37    Ok(())
38}
39
40/// Validates a function name to ensure it doesn't contain invalid characters
41///
42/// # Rules
43/// - Must not be empty
44/// - Must not exceed 64 characters
45/// - Must start with a lowercase letter
46/// - Can only contain: lowercase letters (a-z), digits (0-9), and underscores (_)
47pub fn validate_function_name(name: &str) -> Result<(), BoxError> {
48    if name.is_empty() {
49        return Err("empty string".into());
50    }
51
52    if name.len() > 64 {
53        return Err("string length exceeds the limit 64".into());
54    }
55
56    let mut iter = name.chars();
57    if !matches!(iter.next(), Some('a'..='z')) {
58        return Err("name must start with a lowercase letter".into());
59    }
60
61    for c in iter {
62        if !matches!(c, 'a'..='z' | '0'..='9' | '_' ) {
63            return Err(format!("invalid character: {}", c).into());
64        }
65    }
66    Ok(())
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn test_path_lowercase() {
75        let a = Path::from("a/Foo");
76        assert_eq!(path_lowercase(&a).as_ref(), "a/foo");
77    }
78
79    #[test]
80    fn test_validate_path_part() {
81        assert!(validate_path_part("foo").is_ok());
82        assert!(validate_path_part("fOO").is_ok());
83        assert!(validate_path_part("").is_err());
84        assert!(validate_path_part("foo/").is_err());
85        assert!(validate_path_part("/foo").is_err());
86        assert!(validate_path_part("foo/bar").is_err());
87        assert!(validate_path_part("foo/bar/").is_err());
88    }
89}