14 - Cargo workspace
workspace
crate
Cargo workspace
crate** target **
Java workspace Maven pom + modules
Cargo.toml pom[workspace.dependencies]
<dependencyManagement>
| domain/api/ lib.rs | |
| crate crates.io | |
| crate |
app + libs —— crate
flowchart TD W[workspace ] --> A[apps/server<br/>] W --> L1[crates/domain<br/>] W --> L2[crates/persistence<br/>] A --> L1 A --> L2 L2 --> L1
- —— trait crate
flowchart TD K[core-kernel<br/>trait Plugin ] --> P1[plugin-auth] K --> P2[plugin-storage] K --> P3[plugin-http] A[app ] --> K A -..-> P1 A -..-> P2 A -..-> P3
Cargo.toml[workspace]
# Cargo.toml —— [package]
[workspace]
# glob "crates/*"
members = [
"apps/server",
"crates/domain",
"crates/persistence",
]
# exclude workspace
exclude = ["examples/legacy"]
Cargo.locktarget/workspace
[workspace.dependencies]
workspace workspace = true
——""
# Cargo.toml
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
anyhow = "1"
thiserror = "1"
# crate
domain = { path = "crates/domain" }
[workspace.package] #
version = "0.3.0"
edition = "2021"
license = "MIT"# crates/domain/Cargo.toml ——
[package]
name = "domain"
version.workspace = true # workspace
edition.workspace = true
[dependencies]
serde = { workspace = true } # /features
thiserror = { workspace = true }path
[workspace.dependencies]
# apps/server/Cargo.toml
[package]
name = "server" # crate
version.workspace = true
edition.workspace = true
[dependencies]
domain = { workspace = true } #
persistence = { workspace = true } #
axum = { workspace = true }
tokio = { workspace = true }path crates.io cargo version ——
Feature
feature workspace
# Cargo.toml crate feature
[workspace.dependencies]
persistence = { path = "crates/persistence", default-features = false }
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio"] }# crates/persistence/Cargo.toml
[features]
default = []
postgres = ["sqlx/postgres"] # sqlx
mysql = ["sqlx/mysql"]
[dependencies]
sqlx = { workspace = true, optional = true }#
cargo build -p persistence --features postgrescargo —workspace / -p
cargo build #
cargo build --workspace # workspace
cargo build -p domain # -p --package
cargo test --workspace # CI
cargo run -p server #
cargo tree -p server #
cargo doc --workspace --no-deps #
# members glob
cargo new crates/new-module --libcrate workspace
8000 shop-api cratesrc api/models/repository
domain / core / api / server
workspace
mkdir -p apps/server crates/api crates/core crates/domain
cargo init crates/domain --lib
cargo init crates/core --lib
cargo init crates/api --lib
cargo init apps/server --binCargo.toml
[workspace]
members = ["apps/server", "crates/api", "crates/core", "crates/domain"]
resolver = "2" # edition2021
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"
axum = "0.7"
domain = { path = "crates/domain" }
core = { path = "crates/core" }
api = { path = "crates/api" }
[profile.release]
lto = true # crate
strip = true # ** domain corehandler api
main **
// crates/domain/src/lib.rs ——
pub mod user;
// crates/domain/src/user.rs
use serde::{Deserialize, Serialize};
/// + HTTP
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
pub id: u64,
pub name: String,
pub email: String,
}
impl User {
/// domain
pub fn validate(&self) -> Result<(), String> {
if !self.email.contains('@') {
return Err("email ".into());
}
Ok(())
}
}// crates/core/src/lib.rs —— domain
pub mod repo;
pub use repo::UserRepo;
// crates/core/src/repo.rs
use domain::User;
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
/// sqlx
#[derive(Clone, Default)]
pub struct UserRepo {
inner: Arc<RwLock<HashMap<u64, User>>>,
}
impl UserRepo {
pub fn save(&self, user: User) -> Result<(), String> {
user.validate()?; // domain
self.inner.write().unwrap().insert(user.id, user);
Ok(())
}
pub fn find(&self, id: u64) -> Option<User> {
self.inner.read().unwrap().get(&id).cloned()
}
}// crates/api/src/lib.rs —— core + domain HTTP
pub mod routes;
// crates/api/src/routes.rs
use axum::{extract::{Path, State}, http::StatusCode, Json};
use core::UserRepo;
use domain::User;
/// axum handler State
pub async fn get_user(
State(repo): State<UserRepo>,
Path(id): Path<u64>,
) -> Result<Json<User>, StatusCode> {
match repo.find(id) {
Some(u) => Ok(Json(u)),
None => Err(StatusCode::NOT_FOUND),
}
}// apps/server/src/main.rs ——
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// ""
let repo = core::UserRepo::default();
let _ = repo.save(domain::User {
id: 1,
name: "alice".into(),
email: "a@example.com".into(),
});
let app = axum::Router::new()
.route("/users/{id}", axum::routing::get(api::routes::get_user))
.with_state(repo);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app).await?;
Ok(())
}cargo build --workspace #
# api domain/core ——
cargo build -p servercrates.io
workspace domain -> core -> api
# 1. dry-run //
cargo publish -p domain --dry-run
# 2. token
cargo login
# 3.
cargo publish -p domain
cargo publish -p core
cargo publish -p api
# 4. yank
cargo yank --vers 0.3.0 api- path
versioncrate cargo releaserelease-plz- ——workspace path
CI Swatinem/rust-cache
Rust CI GitHub Actions Swatinem/rust-cache
~/.cargo target
# .github/workflows/ci.yml
name: ci
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# rust-cache Cargo.lock key
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# + +
- run: cargo clippy --workspace --all-targets -- -D warnings
- run: cargo test --workspace
- run: cargo build --workspace --releaseclippy
# --all-targets lib/bin/test/example/bench
# -D warnings lint
cargo clippy --workspace --all-targets -- -D warnings
#
cargo clippy -p domain -- -D warnings
#
cargo clippy --workspace --fix --allow-dirtypre-commit hook CI
workspace
- ****workspace = Maven ///target
- ****
[workspace] members/exclude[workspace.dependencies] - ****
{ workspace = true } - ****models->domainrepo->corehandler->apimain
- CISwatinem/rust-cache +
clippy --workspace --all-targets