458 lines
No EOL
13 KiB
Markdown
458 lines
No EOL
13 KiB
Markdown
# Adding Dependencies
|
|
|
|
BotServer is a single-crate Rust application, so all dependencies are managed through the root `Cargo.toml` file. This guide covers how to add, update, and manage dependencies effectively.
|
|
|
|
## Adding a Dependency
|
|
|
|
### Basic Dependency
|
|
|
|
To add a new crate, edit `Cargo.toml` and add it to the `[dependencies]` section:
|
|
|
|
```toml
|
|
[dependencies]
|
|
serde = "1.0"
|
|
```
|
|
|
|
Then update your dependencies:
|
|
|
|
```bash
|
|
cargo build
|
|
```
|
|
|
|
### Dependency with Features
|
|
|
|
Many crates offer optional features that you can enable selectively. The syntax uses curly braces to specify both the version and the features array:
|
|
|
|
```toml
|
|
[dependencies]
|
|
tokio = { version = "1.41", features = ["full"] }
|
|
serde = { version = "1.0", features = ["derive"] }
|
|
```
|
|
|
|
### Version-Specific Dependencies
|
|
|
|
Cargo supports several version constraint formats to control which versions are acceptable. An exact version uses the equals sign prefix, while minimum versions use the greater-than-or-equal operator. The caret symbol indicates compatible versions according to semantic versioning, and wildcards allow any version within a major release:
|
|
|
|
```toml
|
|
[dependencies]
|
|
# Exact version
|
|
diesel = "=2.1.0"
|
|
|
|
# Minimum version
|
|
anyhow = ">=1.0.0"
|
|
|
|
# Compatible version (caret)
|
|
regex = "^1.11"
|
|
|
|
# Wildcard
|
|
uuid = "1.*"
|
|
```
|
|
|
|
### Git Dependencies
|
|
|
|
You can add dependencies directly from Git repositories when you need unreleased features or custom forks. Specify the repository URL along with an optional branch name:
|
|
|
|
```toml
|
|
[dependencies]
|
|
rhai = { git = "https://github.com/therealprof/rhai.git", branch = "features/use-web-time" }
|
|
```
|
|
|
|
For reproducible builds, pin to a specific commit using the `rev` field:
|
|
|
|
```toml
|
|
[dependencies]
|
|
my-crate = { git = "https://github.com/user/repo", rev = "abc123" }
|
|
```
|
|
|
|
You can also reference a tagged release:
|
|
|
|
```toml
|
|
[dependencies]
|
|
my-crate = { git = "https://github.com/user/repo", tag = "v1.0.0" }
|
|
```
|
|
|
|
### Optional Dependencies
|
|
|
|
Some dependencies aren't always needed and can be marked as optional. These won't be compiled unless explicitly enabled through feature flags:
|
|
|
|
```toml
|
|
[dependencies]
|
|
qdrant-client = { version = "1.12", optional = true }
|
|
imap = { version = "3.0.0-alpha.15", optional = true }
|
|
```
|
|
|
|
Then define features that enable them:
|
|
|
|
```toml
|
|
[features]
|
|
vectordb = ["qdrant-client"]
|
|
email = ["imap"]
|
|
```
|
|
|
|
### Platform-Specific Dependencies
|
|
|
|
Certain dependencies are only needed on specific operating systems. Cargo's target configuration syntax lets you conditionally include dependencies based on the compilation target:
|
|
|
|
```toml
|
|
[target.'cfg(unix)'.dependencies]
|
|
libc = "0.2"
|
|
|
|
[target.'cfg(windows)'.dependencies]
|
|
winapi = "0.3"
|
|
|
|
[target.'cfg(target_os = "macos")'.dependencies]
|
|
core-foundation = "0.9"
|
|
```
|
|
|
|
## Existing Dependencies
|
|
|
|
BotServer relies on a comprehensive set of dependencies organized by functionality.
|
|
|
|
### Web Framework
|
|
|
|
The HTTP layer is built on `axum` as the primary web framework, with `tower` providing middleware and service abstractions. The `tower-http` crate adds HTTP-specific middleware for CORS, static file serving, and tracing. At the lowest level, `hyper` handles the HTTP protocol implementation.
|
|
|
|
### Async Runtime
|
|
|
|
Asynchronous execution is powered by `tokio` with its full feature set enabled. Supporting crates include `tokio-stream` for stream utilities, `async-trait` for async trait definitions, `async-stream` for async stream macros, and `async-lock` for asynchronous locking primitives.
|
|
|
|
### Database
|
|
|
|
Database operations use `diesel` as the ORM for PostgreSQL, with `diesel_migrations` handling schema migrations. Connection pooling is managed by `r2d2`, and the `redis` crate provides cache client functionality compatible with both Valkey and Redis.
|
|
|
|
### Storage
|
|
|
|
Cloud storage integration relies on the AWS SDK, with `aws-config` for configuration and `aws-sdk-s3` for S3-compatible storage operations through the drive component. The optional `qdrant-client` crate enables vector database functionality.
|
|
|
|
### Security
|
|
|
|
Cryptographic operations use several specialized crates. Password hashing is handled by `argon2`, encryption by `aes-gcm`, HMAC authentication by `hmac`, and SHA-256 hashing by `sha2`.
|
|
|
|
### Scripting
|
|
|
|
The BASIC interpreter is powered by `rhai`, which provides a safe and fast embedded scripting engine.
|
|
|
|
### Data Formats
|
|
|
|
Serialization and deserialization use `serde` as the core framework, with `serde_json` for JSON support. Additional format support comes from `csv` for CSV parsing and `base64` for Base64 encoding.
|
|
|
|
### Document Processing
|
|
|
|
Document handling includes `pdf-extract` for PDF text extraction, `mailparse` for email parsing, and `zip` for ZIP archive handling.
|
|
|
|
### Communication
|
|
|
|
Network communication uses `reqwest` as the HTTP client. Email functionality is split between `lettre` for SMTP sending and the optional `imap` crate for reading emails. Video conferencing is provided by the `livekit` crate.
|
|
|
|
### Desktop (Optional)
|
|
|
|
Desktop application builds use `tauri` as the framework, along with `tauri-plugin-dialog` for native file dialogs and `tauri-plugin-opener` for opening files and URLs.
|
|
|
|
### Utilities
|
|
|
|
Common utilities include `anyhow` for error handling, `log` and `env_logger` for logging, `tracing` for structured logging, `chrono` for date and time handling, `uuid` for UUID generation, `regex` for regular expressions, and `rand` for random number generation.
|
|
|
|
### Testing
|
|
|
|
Test support comes from `mockito` for HTTP mocking and `tempfile` for temporary file handling.
|
|
|
|
## Adding a New Dependency: Example
|
|
|
|
This walkthrough demonstrates adding JSON Web Token (JWT) support to the project.
|
|
|
|
### 1. Choose a Crate
|
|
|
|
Search on [crates.io](https://crates.io) to find suitable crates:
|
|
|
|
```bash
|
|
cargo search jsonwebtoken
|
|
```
|
|
|
|
### 2. Add to Cargo.toml
|
|
|
|
```toml
|
|
[dependencies]
|
|
jsonwebtoken = "9.2"
|
|
```
|
|
|
|
### 3. Update Dependencies
|
|
|
|
```bash
|
|
cargo build
|
|
```
|
|
|
|
### 4. Import in Code
|
|
|
|
In your Rust file (e.g., `src/auth/mod.rs`):
|
|
|
|
```rust
|
|
use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};
|
|
```
|
|
|
|
### 5. Use the Dependency
|
|
|
|
```rust
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
struct Claims {
|
|
sub: String,
|
|
exp: usize,
|
|
}
|
|
|
|
pub fn create_jwt(user_id: &str) -> Result<String, jsonwebtoken::errors::Error> {
|
|
let expiration = chrono::Utc::now()
|
|
.checked_add_signed(chrono::Duration::hours(24))
|
|
.unwrap()
|
|
.timestamp() as usize;
|
|
|
|
let claims = Claims {
|
|
sub: user_id.to_owned(),
|
|
exp: expiration,
|
|
};
|
|
|
|
let secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "secret".to_string());
|
|
let token = encode(
|
|
&Header::default(),
|
|
&claims,
|
|
&EncodingKey::from_secret(secret.as_ref()),
|
|
)?;
|
|
|
|
Ok(token)
|
|
}
|
|
```
|
|
|
|
## Managing Dependencies
|
|
|
|
### Updating Dependencies
|
|
|
|
To update all dependencies to their latest compatible versions, run `cargo update`. For updating a specific dependency, use `cargo update -p serde` with the package name.
|
|
|
|
### Checking for Outdated Dependencies
|
|
|
|
The `cargo-outdated` tool helps identify dependencies that have newer versions available:
|
|
|
|
```bash
|
|
cargo install cargo-outdated
|
|
cargo outdated
|
|
```
|
|
|
|
### Upgrading to Latest Compatible Versions
|
|
|
|
The `cargo-edit` tool provides convenient commands for managing dependencies:
|
|
|
|
```bash
|
|
cargo install cargo-edit
|
|
cargo upgrade
|
|
```
|
|
|
|
### Auditing for Security Vulnerabilities
|
|
|
|
Regular security audits are essential for production applications:
|
|
|
|
```bash
|
|
cargo install cargo-audit
|
|
cargo audit
|
|
```
|
|
|
|
### Viewing the Dependency Tree
|
|
|
|
Understanding your dependency graph helps identify bloat and conflicts:
|
|
|
|
```bash
|
|
cargo tree
|
|
```
|
|
|
|
To view dependencies for a specific package:
|
|
|
|
```bash
|
|
cargo tree -p diesel
|
|
```
|
|
|
|
### Finding Duplicate Dependencies
|
|
|
|
Different versions of the same crate increase binary size and compile time:
|
|
|
|
```bash
|
|
cargo tree --duplicates
|
|
```
|
|
|
|
## Feature Management
|
|
|
|
BotServer uses feature flags to enable optional functionality, allowing users to compile only what they need.
|
|
|
|
### Current Features
|
|
|
|
```toml
|
|
[features]
|
|
default = ["desktop"]
|
|
vectordb = ["qdrant-client"]
|
|
email = ["imap"]
|
|
desktop = ["dep:tauri", "dep:tauri-plugin-dialog", "dep:tauri-plugin-opener"]
|
|
```
|
|
|
|
### Adding a New Feature
|
|
|
|
Start by adding the dependency as optional:
|
|
|
|
```toml
|
|
[dependencies]
|
|
elasticsearch = { version = "8.5", optional = true }
|
|
```
|
|
|
|
Then create a feature that enables it:
|
|
|
|
```toml
|
|
[features]
|
|
search = ["elasticsearch"]
|
|
```
|
|
|
|
Use conditional compilation in your code to only include the functionality when the feature is enabled:
|
|
|
|
```rust
|
|
#[cfg(feature = "search")]
|
|
pub mod search {
|
|
use elasticsearch::Elasticsearch;
|
|
|
|
pub fn create_client() -> Elasticsearch {
|
|
// Implementation
|
|
}
|
|
}
|
|
```
|
|
|
|
Build with the feature enabled:
|
|
|
|
```bash
|
|
cargo build --features search
|
|
```
|
|
|
|
## Build Dependencies
|
|
|
|
Dependencies needed only at build time (used in `build.rs`) go in a separate section:
|
|
|
|
```toml
|
|
[build-dependencies]
|
|
tauri-build = { version = "2", features = [] }
|
|
```
|
|
|
|
## Development Dependencies
|
|
|
|
Dependencies needed only during testing should be placed in the dev-dependencies section. These are not included in release builds:
|
|
|
|
```toml
|
|
[dev-dependencies]
|
|
mockito = "1.7.0"
|
|
tempfile = "3"
|
|
```
|
|
|
|
## Dependency Best Practices
|
|
|
|
### Version Pinning
|
|
|
|
For production builds, prefer specific versions over ranges to ensure reproducible builds. While `serde = "1.0.193"` guarantees a specific version, `serde = "1"` could pull in any 1.x release, potentially introducing unexpected changes.
|
|
|
|
### Minimize Dependencies
|
|
|
|
Every dependency you add increases build time, binary size, and maintenance burden while introducing potential security risks. Only add dependencies that provide significant value and aren't easily implemented inline.
|
|
|
|
### Check License Compatibility
|
|
|
|
All dependencies must have licenses compatible with AGPL-3.0. The `cargo-license` tool helps audit your dependency licenses:
|
|
|
|
```bash
|
|
cargo install cargo-license
|
|
cargo license
|
|
```
|
|
|
|
### Prefer Maintained Crates
|
|
|
|
When choosing between crates that provide similar functionality, evaluate them based on recent release activity, GitHub repository engagement, maintainer responsiveness, and documentation quality.
|
|
|
|
### Review Security Advisories
|
|
|
|
Make dependency auditing part of your regular development workflow. Running `cargo audit` regularly helps catch known vulnerabilities before they become problems.
|
|
|
|
### Use Features to Reduce Size
|
|
|
|
Many crates include features you don't need. Instead of enabling everything with `tokio = "1.41"`, specify only the features you actually use:
|
|
|
|
```toml
|
|
tokio = { version = "1.41", features = ["rt-multi-thread", "net", "sync"] }
|
|
```
|
|
|
|
## Common Issues
|
|
|
|
### Conflicting Versions
|
|
|
|
When multiple crates require different versions of the same dependency, Cargo will fail to resolve the dependency graph. Use `cargo tree` to identify which crates are causing the conflict, then update dependencies or look for alternative crates.
|
|
|
|
### Missing System Libraries
|
|
|
|
Some crates require system libraries to be installed. If you see linker errors mentioning `cc`, check the crate's documentation for required system packages and refer to the Building from Source guide.
|
|
|
|
### Feature Not Found
|
|
|
|
Referencing a non-existent feature will cause a build error. Double-check feature names in the crate's `Cargo.toml` on crates.io or in its repository.
|
|
|
|
## Removing Dependencies
|
|
|
|
To remove a dependency, first delete it from `Cargo.toml`. Then find and remove all import statements using grep or ripgrep:
|
|
|
|
```bash
|
|
rg "use dependency_name" src/
|
|
```
|
|
|
|
After removing the imports, clean and rebuild:
|
|
|
|
```bash
|
|
cargo clean
|
|
cargo build
|
|
```
|
|
|
|
Verify the dependency is completely removed:
|
|
|
|
```bash
|
|
cargo tree | grep dependency_name
|
|
```
|
|
|
|
## Alternative Registries
|
|
|
|
### Using a Custom Registry
|
|
|
|
For private crates or custom registries, configure the registry in your `Cargo.toml`:
|
|
|
|
```toml
|
|
[dependencies]
|
|
my-crate = { version = "1.0", registry = "my-registry" }
|
|
|
|
[registries.my-registry]
|
|
index = "https://my-registry.example.com/index"
|
|
```
|
|
|
|
For private company crates, consider Git dependencies or a private registry like Artifactory or CloudSmith.
|
|
|
|
## Dependency Documentation
|
|
|
|
Good documentation makes dependencies easier to maintain. Add comments in `Cargo.toml` explaining why each dependency exists:
|
|
|
|
```toml
|
|
[dependencies]
|
|
# JWT token generation and validation
|
|
jsonwebtoken = "9.2"
|
|
```
|
|
|
|
Document usage in your code with doc comments that explain the dependency's role:
|
|
|
|
```rust
|
|
/// Creates a JWT token for user authentication.
|
|
///
|
|
/// Uses the `jsonwebtoken` crate to encode user claims
|
|
/// with an expiration time.
|
|
pub fn create_jwt(user_id: &str) -> Result<String, Error> {
|
|
// Implementation
|
|
}
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
Review the [Module Structure](./crates.md) documentation to understand where to use new dependencies within the codebase. The [Service Layer](./services.md) guide shows how dependencies integrate into the application architecture. For extending BASIC with new functionality that leverages dependencies, see [Creating Custom Keywords](./custom-keywords.md). |