Skip to content

Commit 13dcb6c

Browse files
committed
Slim default dependency tree for rustapi-rs
Remove tracing-subscriber from rustapi-core production dependencies and stop auto-initializing it in RustApi::new(); apps and cargo-rustapi templates already set up tracing in main. Gate rust-i18n behind the validate i18n feature with English fallbacks, exposing core-i18n on rustapi-rs. Default builds drop from ~259 to ~158 transitive crates. Also align rustapi-openapi on workspace rustapi-macros version.
1 parent b6a9531 commit 13dcb6c

7 files changed

Lines changed: 68 additions & 38 deletions

File tree

crates/rustapi-core/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ tower-service = { workspace = true }
3737

3838
# Utilities
3939
tracing = { workspace = true }
40-
tracing-subscriber = { workspace = true }
4140
linkme = { workspace = true }
4241
uuid = { workspace = true }
4342

@@ -76,6 +75,7 @@ async-trait = { workspace = true, optional = true }
7675
dashmap = { version = "6.0", optional = true }
7776

7877
[dev-dependencies]
78+
tracing-subscriber = { workspace = true }
7979
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
8080
proptest = "1.4"
8181
rustapi-testing = { workspace = true }

crates/rustapi-core/src/app/builder.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,13 @@ use crate::middleware::{LayerStack, MiddlewareLayer, DEFAULT_BODY_LIMIT};
77
use crate::router::Router;
88
use std::future::Future;
99
use std::sync::Arc;
10-
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
1110

1211
impl RustApi {
13-
/// Create a new RustAPI application
12+
/// Create a new RustAPI application.
13+
///
14+
/// Initialize `tracing-subscriber` in `main` when using the `tracing` feature;
15+
/// `cargo-rustapi` templates already do this.
1416
pub fn new() -> Self {
15-
// Initialize tracing if not already done
16-
let _ = tracing_subscriber::registry()
17-
.with(
18-
EnvFilter::try_from_default_env()
19-
.unwrap_or_else(|_| EnvFilter::new("info,rustapi=debug")),
20-
)
21-
.with(tracing_subscriber::fmt::layer())
22-
.try_init();
23-
2417
Self {
2518
router: Router::new(),
2619
openapi_spec: rustapi_openapi::OpenApiSpec::new("RustAPI Application", "1.0.0")

crates/rustapi-openapi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ serde = { workspace = true }
2020
serde_json = { workspace = true }
2121

2222
# Macros
23-
rustapi-macros = { path = "../rustapi-macros", version = "0.1.300" }
23+
rustapi-macros = { workspace = true }
2424

2525
[features]
2626
default = ["swagger-ui"]

crates/rustapi-rs/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ core-cookies = ["dep:rustapi-extras", "rustapi-extras/cookies", "rustapi-core/co
6060
core-http3 = ["rustapi-core/http3"]
6161
core-http3-dev = ["rustapi-core/http3-dev"]
6262
core-dashboard = ["rustapi-core/dashboard"]
63+
core-i18n = ["rustapi-validate/i18n"]
6364

6465
# Canonical protocol features
6566
protocol-toon = ["dep:rustapi-toon"]
@@ -159,7 +160,8 @@ oauth2-client = ["extras-oauth2-client"]
159160
session = ["extras-session"]
160161
session-redis = ["extras-session-redis"]
161162
jobs = ["extras-jobs"]
163+
i18n = ["core-i18n"]
162164
extras = ["extras-jwt", "extras-cors", "extras-rate-limit"]
163165

164166
# Canonical aggregate
165-
full = ["core", "protocol-all", "extras-all", "core-legacy-validator", "core-dashboard"]
167+
full = ["core", "protocol-all", "extras-all", "core-legacy-validator", "core-dashboard", "core-i18n"]

crates/rustapi-validate/Cargo.toml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,23 @@ async-trait = { workspace = true }
2525
# Regex for pattern validation
2626
regex = "1.10"
2727

28-
# Internationalization
29-
rust-i18n = "3.0"
28+
# Internationalization (optional — English fallbacks ship without this)
29+
rust-i18n = { version = "3.0", optional = true }
3030

3131
# Re-export derive macro
3232
rustapi-macros = { workspace = true }
3333

3434
[dev-dependencies]
3535
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
3636
proptest = "1.4"
37-
rust-i18n = "3.0"
3837
rustapi-core = { workspace = true, default-features = false }
3938

39+
[features]
40+
default = []
41+
i18n = ["dep:rust-i18n"]
42+
43+
[[test]]
44+
name = "i18n"
45+
path = "tests/i18n.rs"
46+
required-features = ["i18n"]
47+

crates/rustapi-validate/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
//! }
7272
//! ```
7373
74-
// Load I18n locales
74+
#[cfg(feature = "i18n")]
7575
rust_i18n::i18n!("locales");
7676

7777
pub mod custom;
Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#[cfg(feature = "i18n")]
12
use rust_i18n::t;
23

34
/// Helper to translate a message.
@@ -9,31 +10,57 @@ use rust_i18n::t;
910
/// * `key` - The message key (e.g. "validation.email.invalid")
1011
/// * `locale` - The locale to use (e.g. "en", "tr"). If None, uses default.
1112
pub fn translate(key: &str, locale: Option<&str>) -> String {
12-
let result = if let Some(locale) = locale {
13-
t!(key, locale = locale).to_string()
14-
} else {
15-
t!(key).to_string()
16-
};
13+
#[cfg(feature = "i18n")]
14+
{
15+
let result = if let Some(locale) = locale {
16+
t!(key, locale = locale).to_string()
17+
} else {
18+
t!(key).to_string()
19+
};
1720

18-
// Fallback to English if translation is missing (returns key)
19-
if result == key {
20-
t!(key, locale = "en").to_string()
21-
} else {
22-
result
21+
if result == key {
22+
t!(key, locale = "en").to_string()
23+
} else {
24+
result
25+
}
26+
}
27+
28+
#[cfg(not(feature = "i18n"))]
29+
{
30+
let _ = locale;
31+
fallback_message(key)
2332
}
2433
}
2534

2635
/// Helper to translate with arguments.
2736
pub fn translate_with_args(key: &str, locale: Option<&str>, _args: &[(&str, &str)]) -> String {
28-
if let Some(locale) = locale {
29-
// rust-i18n t! macro doesn't support dynamic args easily in this wrapped form
30-
// We might need to use the lower level API or just interpolate ourselves
31-
// For now let's use the basic t! with variable interpolation if possible
32-
// But t! requires string literals for keys mostly or known args.
33-
// Let's stick to basic translation for now and use our existing interpolation
34-
// in RuleError for variable replacement.
35-
t!(key, locale = locale).to_string()
36-
} else {
37-
t!(key).to_string()
37+
translate(key, locale)
38+
}
39+
40+
#[cfg(not(feature = "i18n"))]
41+
fn fallback_message(key: &str) -> String {
42+
match key {
43+
"validation.email.invalid" => "Invalid email format",
44+
"validation.length.min" => "Length must be at least %{min} characters",
45+
"validation.length.max" => "Length must be at most %{max} characters",
46+
"validation.length.exact" => "Length must be exactly %{len} characters",
47+
"validation.range.min" => "Value must be at least %{min}",
48+
"validation.range.max" => "Value must be at most %{max}",
49+
"validation.range.between" => "Value must be between %{min} and %{max}",
50+
"validation.required.missing" => "This field is required",
51+
"validation.url.invalid" => "Invalid URL format",
52+
"validation.regex.mismatch" => "Value does not match pattern: %{pattern}",
53+
"validation.unique.taken" => "This value is already taken",
54+
"validation.exists.not_found" => "Value does not exist",
55+
"validation.api.invalid" => "External validation failed",
56+
"validation.credit_card.invalid_format" => "Invalid credit card format",
57+
"validation.credit_card.invalid" => "Invalid credit card number",
58+
"validation.ip.v4_required" => "IPv4 address required",
59+
"validation.ip.v6_required" => "IPv6 address required",
60+
"validation.ip.invalid" => "Invalid IP address",
61+
"validation.phone.invalid" => "Invalid phone number",
62+
"validation.contains.missing" => "Required value is missing",
63+
other => other,
3864
}
65+
.to_string()
3966
}

0 commit comments

Comments
 (0)