diff --git a/Cargo.lock b/Cargo.lock index a251745..da6db3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,7 @@ dependencies = [ "dirs", "fast_html2md", "futures", + "hashbrown 0.16.1", "lettre", "mail-parser", "native-tls", @@ -674,6 +675,25 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -867,6 +887,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "email-encoding" version = "0.4.1" @@ -1245,6 +1271,9 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash 0.2.0", + "rayon", + "serde", + "serde_core", ] [[package]] @@ -1769,6 +1798,15 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.29" @@ -2081,6 +2119,29 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + [[package]] name = "pastey" version = "0.2.1" @@ -2477,6 +2538,35 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.5.2" @@ -2780,6 +2870,12 @@ dependencies = [ "syn", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "secret-lib" version = "1.0.0" @@ -3240,6 +3336,7 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", diff --git a/Cargo.toml b/Cargo.toml index 2786b5b..37cdbef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,29 @@ [workspace] resolver = "2" members = ["crates/agentmail", "crates/agentmail-mcp"] + +[workspace.dependencies] +agentmail = { path = "crates/agentmail" } +chrono = { version = "0.4", features = ["serde"] } +clap = { version = "4", features = ["derive"] } +dirs = "6" +futures = "0.3" +hashbrown = { version = "0.16", features = ["rayon", "serde"] } +reqwest = { version = "0.13", features = ["json"] } +rmcp = "1.2" +schemars = { version = "1.2", features = ["chrono04"] } +secret-lib = { version = "1", features = ["keyring", "rustls", "tokio"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +thiserror = "2" +tokio = { version = "1", features = ["full"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +uuid = { version = "1", features = ["v4"] } +async-imap = { version = "0.11", default-features = false } +fast_html2md = "0.0" +lettre = { version = "0.11", default-features = false } +mail-parser = "0.11" +native-tls = "0.2" +tokio-native-tls = "0.3" +toml = "1.1" diff --git a/crates/agentmail-mcp/Cargo.toml b/crates/agentmail-mcp/Cargo.toml index 3701e2a..8ed51cb 100644 --- a/crates/agentmail-mcp/Cargo.toml +++ b/crates/agentmail-mcp/Cargo.toml @@ -22,21 +22,13 @@ name = "agentmail" path = "src/main.rs" [dependencies] -agentmail = { path = "../agentmail" } -clap = { version = "4", features = ["derive"] } -rmcp = { version = "1.2", default-features = true, features = [ - "transport-async-rw", - "transport-worker", -] } -schemars = "1.2" -secret-lib = { version = "1", features = ["keyring", "rustls", "tokio"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -tokio = { version = "1", features = [ - "io-std", - "macros", - "rt", - "rt-multi-thread" -] } -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -uuid = { version = "1", features = ["v4"] } +agentmail.workspace = true +clap.workspace = true +rmcp = { workspace = true, features = ["transport-async-rw", "transport-worker"] } +schemars.workspace = true +secret-lib.workspace = true +serde.workspace = true +serde_json.workspace = true +tokio = { workspace = true, features = ["io-std", "macros", "rt", "rt-multi-thread"] } +tracing-subscriber.workspace = true +uuid.workspace = true diff --git a/crates/agentmail/Cargo.toml b/crates/agentmail/Cargo.toml index 2999ad7..a1b468d 100644 --- a/crates/agentmail/Cargo.toml +++ b/crates/agentmail/Cargo.toml @@ -8,49 +8,35 @@ repository = "https://github.com/weekendsuperhero-io/agentmail" documentation = "https://docs.rs/agentmail" homepage = "https://github.com/weekendsuperhero-io/agentmail" readme = "../../README.md" -keywords = ["email", "imap", "mail", "mcp", "agent"] +keywords = ["agent", "email", "imap", "mail", "mcp"] categories = ["email", "network-programming"] authors = ["WeekendSuperhero "] rust-version = "1.94" [dependencies] # IMAP protocol (tokio runtime, no async-std) -async-imap = { version = "0.11", default-features = false, features = [ - "runtime-tokio" -] } -# Date/time handling -chrono = { version = "0.4", features = ["serde"] } -# Directory paths -dirs = "6" +async-imap = { workspace = true, features = ["runtime-tokio"] } +chrono.workspace = true +dirs.workspace = true # HTML to markdown conversion -fast_html2md = "0.0" -# Async utilities -futures = "0.3" +fast_html2md = { workspace = true } +futures.workspace = true +hashbrown.workspace = true # RFC822 message composition (for drafts) -lettre = { version = "0.11", default-features = false, features = ["builder"] } +lettre = { workspace = true, features = ["builder"] } # Email parsing from raw RFC822 -mail-parser = "0.11" -native-tls = "0.2" -# HTTP client (for RFC 8058 one-click unsubscribe) -reqwest = { version = "0.13", features = ["json"] } +mail-parser = { workspace = true } +native-tls = { workspace = true } +reqwest.workspace = true +schemars.workspace = true # Credential storage (raw, shell command, or system keyring) -secret-lib = { version = "1", features = [ - "command", - "derive", - "keyring", - "rustls", - "tokio" -] } -schemars = { version = "1.2", features = ["chrono04"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -# Error handling -thiserror = "2" -# Async runtime -tokio = { version = "1", features = ["fs", "net", "rt", "sync", "time"] } +secret-lib = { workspace = true, features = ["command", "derive"] } +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +tokio.workspace = true # TLS for IMAP connections (tokio-native) -tokio-native-tls = "0.3" +tokio-native-tls = { workspace = true } # Configuration file parsing -toml = "1.1" -# Logging -tracing = "0.1" +toml = { workspace = true } +tracing.workspace = true diff --git a/crates/agentmail/src/config.rs b/crates/agentmail/src/config.rs index 96e3e9d..962ca10 100644 --- a/crates/agentmail/src/config.rs +++ b/crates/agentmail/src/config.rs @@ -1,6 +1,6 @@ +use hashbrown::HashMap; use secret::Secret; use serde::Deserialize; -use std::collections::HashMap; use std::path::PathBuf; /// Top-level configuration file. diff --git a/crates/agentmail/src/connection.rs b/crates/agentmail/src/connection.rs index 9c7b6a5..3a91fc8 100644 --- a/crates/agentmail/src/connection.rs +++ b/crates/agentmail/src/connection.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use hashbrown::HashMap; use std::sync::Arc; use tokio::sync::{Mutex, OwnedSemaphorePermit, Semaphore}; diff --git a/crates/agentmail/src/imap_client.rs b/crates/agentmail/src/imap_client.rs index 52eaaae..7861bcb 100644 --- a/crates/agentmail/src/imap_client.rs +++ b/crates/agentmail/src/imap_client.rs @@ -1005,8 +1005,8 @@ pub async fn fetch_flags( let uids_raw = imap_timeout(session.uid_search("ALL")).await?; let uids: Vec = uids_raw.into_iter().collect(); let total = uids.len() as u64; - let mut flag_counts: std::collections::HashMap = std::collections::HashMap::new(); - let mut color_counts: std::collections::HashMap = std::collections::HashMap::new(); + let mut flag_counts: hashbrown::HashMap = hashbrown::HashMap::new(); + let mut color_counts: hashbrown::HashMap = hashbrown::HashMap::new(); let mut completed = 0u64; for chunk in uids.chunks(1000) { diff --git a/crates/agentmail/src/lib.rs b/crates/agentmail/src/lib.rs index 4f5d1f9..0c2187c 100644 --- a/crates/agentmail/src/lib.rs +++ b/crates/agentmail/src/lib.rs @@ -290,7 +290,7 @@ impl Agentmail { None => list_scannable_mailbox_names(session.session()).await?, }; - use std::collections::HashMap; + use hashbrown::HashMap; // Key by (email, display_name) so "Find My " and // "iCloud " are separate entries. let mut map: HashMap<(String, String), SenderSummary> = HashMap::new(); @@ -379,7 +379,7 @@ impl Agentmail { None => list_scannable_mailbox_names(session.session()).await?, }; - use std::collections::HashMap; + use hashbrown::HashMap; use types::ListSummary; // Key by (email, display_name) for exact sender grouping @@ -712,7 +712,7 @@ impl Agentmail { None => list_scannable_mailbox_names(session.session()).await?, }; - use std::collections::HashMap; + use hashbrown::HashMap; let mut total_flags: HashMap = HashMap::new(); let mut total_colors: HashMap = HashMap::new(); let mut per_mailbox = Vec::new(); diff --git a/crates/agentmail/src/parser.rs b/crates/agentmail/src/parser.rs index 83f4cec..2d70c7f 100644 --- a/crates/agentmail/src/parser.rs +++ b/crates/agentmail/src/parser.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use hashbrown::HashMap; use mail_parser::{MessageParser, MimeHeaders}; diff --git a/crates/agentmail/src/types.rs b/crates/agentmail/src/types.rs index 186762d..68b45a9 100644 --- a/crates/agentmail/src/types.rs +++ b/crates/agentmail/src/types.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use hashbrown::HashMap; use chrono::{DateTime, Utc}; use schemars::JsonSchema; @@ -95,6 +95,7 @@ pub struct MessageInfo { // All headers (raw original values) #[serde(skip_serializing_if = "HashMap::is_empty", default)] + #[schemars(with = "std::collections::HashMap>")] pub headers: HashMap>, }