From baac91713579cc6d1f36ae78ea4223d2a90d0aa6 Mon Sep 17 00:00:00 2001 From: TheWeirdDee Date: Wed, 26 Aug 2026 14:37:19 +0100 Subject: [PATCH 1/6] refactor(lint): replace crate-wide lint allowances with scoped exceptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/lib.rs and src/main.rs carried a blanket matching [lints] table listing 16 individually-named lints. Together these silenced every default Clippy lint group (correctness, suspicious, style, complexity, perf) plus dead_code/unused_imports/ unused_variables across the entire crate — not just the handful of patterns the comments claimed to justify. Removing both surfaced ~2021 warnings under `cargo clippy --all-features`. Of those: - 94% (1658) were the single mechanical `uninlined_format_args` style lint, applied via `cargo clippy --fix` (two passes to converge, after fixing one clippy suggestion that didn't actually compile: removing `.to_string()` from a String/&str comparison in audit.rs that only worked because it allocated a matching String). - ~160 more were fixed by hand: manual_clamp, ptr_arg (with the attendant &PathBuf/&Path .clone() semantics fix at each call site), needless_range_loop, vec_init_then_push, if_same_then_else (two genuine redundant-branch merges), a should_implement_trait rename, a handful of type_complexity type aliases, and other one-off style fixes. - One of those fixes was a real bug, not just style: a constructed template changelog entry was built and then silently discarded (`changelog: None` instead of `Some(changelog)`) in templates.rs — vec_init_then_push flagged the construction, but the value was never used at all. - The remaining 55 (26 too_many_arguments, 29 dead_code) are now scoped #[allow(...)] on the specific function/struct/field that needs it, each with a comment explaining why, instead of a crate-wide suppression that hid everything else behind them. Also fixes two default-build-only warnings that were only ever hidden by the same blanket: hardware_wallet.rs's Ledger APDU codec helpers are dead when built without --features hardware-wallet and without tests (now an honest, scoped cfg_attr instead of silence), and profiler.rs had a genuinely pointless `#[cfg(not(feature = "memory-profiling"))] let memory_tracker = None;` plus its supporting placeholder struct, neither of which anything ever used — deleted outright rather than allowed. `cargo clippy --all-features --locked -- -D warnings` (exactly what CI runs) now passes with zero warnings, with every remaining exception scoped and documented rather than silenced project-wide. Updates CODE_STYLE_STANDARDS.md's "Project-Specific Allowances" section, which previously described the now-removed blanket as if it only covered a few narrow cases. --- CODE_STYLE_STANDARDS.md | 56 ++++--- Cargo.lock | 1 + Cargo.toml | 24 +-- src/commands/ai.rs | 3 + src/commands/ai_accessibility.rs | 27 +++- src/commands/ai_audit.rs | 4 +- src/commands/ai_cache_cmd.rs | 10 +- src/commands/ai_chat.rs | 5 +- src/commands/ai_deployment_test.rs | 5 +- src/commands/ai_error.rs | 5 +- src/commands/ai_feedback.rs | 2 - src/commands/ai_model_router.rs | 20 ++- src/commands/ai_plan.rs | 16 +- src/commands/ai_property_test.rs | 15 +- src/commands/ai_recommend.rs | 2 +- src/commands/ai_search.rs | 1 - src/commands/ai_test.rs | 16 +- src/commands/ai_test_gen.rs | 6 +- src/commands/ai_tutorial_cmd.rs | 4 +- src/commands/analytics.rs | 5 +- src/commands/approval.rs | 10 +- src/commands/autocomplete.rs | 4 +- src/commands/bridge.rs | 2 +- src/commands/collab.rs | 4 +- src/commands/compliance.rs | 10 +- src/commands/config.rs | 14 +- src/commands/deploy.rs | 4 +- src/commands/deployment_automate.rs | 6 +- src/commands/deployment_optimize.rs | 2 +- src/commands/deployments.rs | 4 +- src/commands/docs.rs | 4 + src/commands/feature_flags_cmd.rs | 9 +- src/commands/governance.rs | 4 +- src/commands/help.rs | 9 +- src/commands/migrate_ai.rs | 16 +- src/commands/mod.rs | 4 +- src/commands/monitor.rs | 18 ++- src/commands/multi_network.rs | 4 +- src/commands/network.rs | 4 +- src/commands/new.rs | 25 ++++ src/commands/nl.rs | 98 ++++++------ src/commands/optimize.rs | 12 +- src/commands/perf.rs | 1 - src/commands/plugin.rs | 9 +- src/commands/privacy.rs | 3 +- src/commands/project.rs | 9 +- src/commands/recommend.rs | 2 +- src/commands/registry.rs | 4 + src/commands/security.rs | 10 +- src/commands/social.rs | 1 - src/commands/template.rs | 20 ++- src/commands/template_security.rs | 4 +- src/commands/test.rs | 4 +- src/commands/upgrade_auto.rs | 14 +- src/commands/wallet.rs | 18 ++- src/lib.rs | 2 - src/main.rs | 4 +- src/plugins/loader.rs | 5 +- src/plugins/registry.rs | 5 +- src/utils/ai.rs | 5 +- src/utils/ai_accessibility.rs | 27 ++-- src/utils/ai_cache.rs | 10 +- src/utils/ai_context.rs | 5 +- src/utils/ai_conversation.rs | 35 ++--- src/utils/ai_deployment_planner.rs | 18 ++- src/utils/ai_doc_qa.rs | 63 ++++---- src/utils/ai_docs.rs | 4 + src/utils/ai_error_handler.rs | 16 +- src/utils/ai_feedback.rs | 4 +- src/utils/ai_gas_estimation.rs | 3 + src/utils/ai_model_router.rs | 94 ++++++------ src/utils/ai_project_planner.rs | 46 ++++-- src/utils/ai_property_testing.rs | 6 +- src/utils/ai_recommendations.rs | 4 +- src/utils/ai_refactor.rs | 7 +- src/utils/ai_search.rs | 10 +- src/utils/ai_telemetry.rs | 7 +- src/utils/ai_test_analytics.rs | 6 + src/utils/ai_test_assistant.rs | 13 +- src/utils/ai_test_generator.rs | 14 +- src/utils/ai_tutorial.rs | 12 +- src/utils/ai_validation.rs | 3 +- src/utils/approval_engine.rs | 4 + src/utils/audit.rs | 8 +- src/utils/bindings.rs | 35 +++-- src/utils/bridge/providers.rs | 2 +- src/utils/compliance.rs | 13 +- src/utils/context_help.rs | 15 +- src/utils/contract_assertions.rs | 2 +- src/utils/contract_deps.rs | 2 +- src/utils/contract_health_monitor.rs | 2 +- src/utils/contract_test_framework.rs | 22 +-- src/utils/cost_management.rs | 4 +- src/utils/crypto.rs | 5 +- src/utils/database.rs | 149 +++++++++++-------- src/utils/debugger.rs | 3 + src/utils/deployment_automation.rs | 6 +- src/utils/deployment_monitor.rs | 10 +- src/utils/deployment_monitoring_service.rs | 1 - src/utils/doc_generator.rs | 8 +- src/utils/doc_templates.rs | 2 +- src/utils/docs.rs | 5 +- src/utils/documentation.rs | 12 +- src/utils/feature_flags.rs | 7 +- src/utils/gas_analyzer.rs | 2 +- src/utils/hardware_wallet.rs | 8 + src/utils/migration_ai.rs | 32 ++-- src/utils/network_simulator/deterministic.rs | 1 - src/utils/network_simulator/failure.rs | 1 - src/utils/network_simulator/scenarios.rs | 4 +- src/utils/network_simulator/simulator.rs | 7 +- src/utils/network_simulator/state.rs | 6 +- src/utils/network_simulator/time.rs | 2 +- src/utils/notifications.rs | 4 +- src/utils/orchestration.rs | 11 +- src/utils/pattern_library.rs | 3 +- src/utils/performance.rs | 6 +- src/utils/profiler.rs | 10 +- src/utils/prompt_manager.rs | 5 +- src/utils/quality_analysis.rs | 11 +- src/utils/registry.rs | 1 - src/utils/repl.rs | 1 - src/utils/scheduler.rs | 4 + src/utils/security/ai_audit.rs | 16 +- src/utils/security/ai_audit_service.rs | 3 +- src/utils/security/audit.rs | 4 + src/utils/security/compliance.rs | 8 +- src/utils/security/data_protection.rs | 12 +- src/utils/security_scanner.rs | 2 +- src/utils/social.rs | 3 +- src/utils/soroban.rs | 2 +- src/utils/state_diff.rs | 1 - src/utils/stream.rs | 4 +- src/utils/template.rs | 10 +- src/utils/template_analytics.rs | 2 +- src/utils/template_customization_ai.rs | 6 +- src/utils/template_performance.rs | 8 +- src/utils/template_recommender.rs | 29 ++-- src/utils/template_security_scanner.rs | 16 +- src/utils/template_version_ai.rs | 5 +- src/utils/templates.rs | 24 +-- src/utils/test_automation.rs | 10 +- src/utils/test_generator.rs | 3 + src/utils/test_optimizer.rs | 9 +- src/utils/testnet_integration.rs | 4 +- 145 files changed, 907 insertions(+), 731 deletions(-) diff --git a/CODE_STYLE_STANDARDS.md b/CODE_STYLE_STANDARDS.md index 707f722a..9c1b88d6 100644 --- a/CODE_STYLE_STANDARDS.md +++ b/CODE_STYLE_STANDARDS.md @@ -481,27 +481,49 @@ cargo clippy -- -W clippy::needless_clone ### Project-Specific Allowances -The StarForge project allows these clippy rules in specific circumstances. See `src/main.rs` for the global allowlist: +**There is no crate-wide lint allowlist.** `src/lib.rs` and `src/main.rs` used to +carry a blanket `#![allow(dead_code, unused, clippy::all)]`, and `Cargo.toml` +carried a matching `[lints]` table — together these silenced essentially every +default Clippy lint group (correctness, suspicious, style, complexity, perf) +across the *entire* ~40k-line crate, not just the handful of patterns the +comments claimed to cover. That's exactly backwards: it hid real bugs (see +below) behind the same blanket that was meant to excuse a few CLI functions +with many arguments. + +Every remaining `#[allow(...)]` in the codebase is now **scoped to the single +item that needs it** — a function, struct, or field — with a comment +immediately above explaining *why*: ```rust -#![allow( - dead_code, // Some plugin infrastructure code is unused until plugins load it - clippy::needless_range_loop, // Sometimes more readable than alternatives - clippy::redundant_closure, // Used intentionally for clarity in some cases - clippy::too_many_arguments, // Complex CLI commands require many arguments - clippy::type_complexity, // Some type definitions are inherently complex - clippy::unnecessary_lazy_evaluations // Some expressions are evaluated for side effects -)] +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] +async fn monitor_contract( + contract_id: &str, + events_filter: Option<&str>, + // ... +) -> Result<()> { ``` -**When to add to this allowlist:** -- Only for **unavoidable** patterns -- Document *why* with a comment -- Discuss with maintainers before merging - -```rust -#![allow(clippy::too_many_arguments)] // Contract CLI requires many parameters for optimization context -``` +**When to add a scoped allow:** +- Only on the specific item that triggers it — never on a module, and never + crate-wide. +- Only for patterns that are genuinely **unavoidable or clearly intentional** + for that one item, not as a shortcut past a warning you haven't looked at. +- Always with a comment explaining *why*, not just restating the lint name. +- `dead_code` is the one lint where "unavoidable" usually means "not wired up + yet, but deleting it isn't this change's call to make" — that's a valid + reason, but say so explicitly rather than leaving the allow unexplained. + +**What restoring this signal found:** with the blanket removed, +`cargo clippy --all-features` went from silently clean to over 2000 +warnings — 94% of them were the single mechanical `uninlined_format_args` +style lint (fixed via `cargo clippy --fix`), but the rest included real +defects the blanket had been hiding, e.g. a constructed template changelog +entry that was built and then silently discarded (`changelog: None` instead +of `Some(changelog)`) and unreachable branches. Local, documented exceptions +don't have that failure mode: each one is small enough to actually read. **When NOT to add:** - "I don't want to refactor" — do the refactor diff --git a/Cargo.lock b/Cargo.lock index 682b5097..7bb8af22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3643,6 +3643,7 @@ dependencies = [ "stellar-strkey", "stellar-xdr", "tempfile", + "thiserror 1.0.69", "tokio", "tokio-tungstenite", "toml", diff --git a/Cargo.toml b/Cargo.toml index 888d6bff..f64d5b8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,29 +16,6 @@ name = "starforge" path = "src/lib.rs" crate-type = ["cdylib", "rlib"] -# Package-wide lint policy. These lints are intentionally relaxed for the crate -# (including integration tests, which use lightweight mock structs and helpers). -# Centralizing them here keeps `cargo clippy --all-targets -- -D warnings` green. -[lints.rust] -dead_code = "allow" -unused_imports = "allow" -unused_variables = "allow" - -[lints.clippy] -needless_range_loop = "allow" -redundant_closure = "allow" -too_many_arguments = "allow" -type_complexity = "allow" -unnecessary_lazy_evaluations = "allow" -items_after_test_module = "allow" -needless_borrow = "allow" -needless_borrows_for_generic_args = "allow" -empty_line_after_doc_comments = "allow" -doc_overindented_list_items = "allow" -expect_fun_call = "allow" -useless_vec = "allow" -single_match = "allow" - [dependencies] clap = { version = "=4.4.18", features = ["derive", "color"] } serde = { version = "1.0", features = ["derive"] } @@ -84,6 +61,7 @@ zip = "0.6" tempfile = "3.8" wasm-bindgen = "0.2" rusqlite = { version = "0.32", features = ["bundled"] } +thiserror = "1" csv = "1.0" minijinja = "1.0" serde_yaml = "0.9.34" diff --git a/src/commands/ai.rs b/src/commands/ai.rs index 85d376d9..d3a6e7b2 100644 --- a/src/commands/ai.rs +++ b/src/commands/ai.rs @@ -475,6 +475,9 @@ async fn handle_ask(question: &str, model: &str, temperature: f32, max_tokens: u Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] async fn handle_translate(text: &str, target: &str, model: &str) -> Result<()> { if text.trim().is_empty() { anyhow::bail!("Please provide text to translate."); diff --git a/src/commands/ai_accessibility.rs b/src/commands/ai_accessibility.rs index 5987c632..437a2875 100644 --- a/src/commands/ai_accessibility.rs +++ b/src/commands/ai_accessibility.rs @@ -190,13 +190,22 @@ fn handle_status(json: bool) -> Result<()> { p::header("Accessibility Configuration"); p::separator(); p::kv("Screen reader mode", &cfg.screen_reader_mode.to_string()); - p::kv("Simplified text mode", &cfg.simplified_text_mode.to_string()); + p::kv( + "Simplified text mode", + &cfg.simplified_text_mode.to_string(), + ); p::kv("High contrast mode", &cfg.high_contrast_mode.to_string()); p::kv("Voice commands", &cfg.voice_commands_enabled.to_string()); - p::kv("Keyboard shortcuts", &cfg.keyboard_shortcuts_enabled.to_string()); + p::kv( + "Keyboard shortcuts", + &cfg.keyboard_shortcuts_enabled.to_string(), + ); p::kv("Reduce motion", &cfg.reduce_motion.to_string()); p::kv("Announce progress", &cfg.announce_progress.to_string()); - p::kv("Verbose descriptions", &cfg.verbose_descriptions.to_string()); + p::kv( + "Verbose descriptions", + &cfg.verbose_descriptions.to_string(), + ); p::kv("Font size", &format!("{:?}", cfg.font_size)); p::separator(); Ok(()) @@ -234,9 +243,9 @@ fn handle_configure(args: ConfigureArgs) -> Result<()> { })?; p::success("Accessibility settings updated."); - if !args.screen_reader.is_none() - || !args.simplified_text.is_none() - || !args.high_contrast.is_none() + if args.screen_reader.is_some() + || args.simplified_text.is_some() + || args.high_contrast.is_some() { p::kv("Screen reader", &cfg.screen_reader_mode.to_string()); p::kv("Simplified text", &cfg.simplified_text_mode.to_string()); @@ -416,7 +425,11 @@ fn handle_toggle(setting: &str, enable: Option) -> Result<()> { _ => false, }; - p::success(&format!("{} mode: {}", label, if state { "enabled" } else { "disabled" })); + p::success(&format!( + "{} mode: {}", + label, + if state { "enabled" } else { "disabled" } + )); Ok(()) } diff --git a/src/commands/ai_audit.rs b/src/commands/ai_audit.rs index 226d3cb8..9920508b 100644 --- a/src/commands/ai_audit.rs +++ b/src/commands/ai_audit.rs @@ -4,9 +4,9 @@ //! with comprehensive coverage and minimal false positives. use crate::utils::print as p; -use crate::utils::security::{AiAuditService, AuditLevel, AuditRequest}; +use crate::utils::security::{AuditLevel, AuditRequest}; use anyhow::Result; -use clap::{Args, Subcommand}; +use clap::Args; use colored::*; use std::fs; use std::path::{Path, PathBuf}; diff --git a/src/commands/ai_cache_cmd.rs b/src/commands/ai_cache_cmd.rs index af95d45e..fd05c3b2 100644 --- a/src/commands/ai_cache_cmd.rs +++ b/src/commands/ai_cache_cmd.rs @@ -11,9 +11,9 @@ //! - `warm` – pre-warm cache with common operations use crate::utils::{ai_cache, print as p}; -use anyhow::{Context, Result}; -use clap::{Args, Subcommand}; -use std::path::PathBuf; +use anyhow::Result; +use clap::Subcommand; +use std::path::{Path, PathBuf}; // ─── Sub-command enum ───────────────────────────────────────────────────────── @@ -288,7 +288,7 @@ async fn handle_invalidate(tags: Option<&str>, model: Option<&str>) -> Result<() Ok(()) } -async fn handle_export(path: &PathBuf) -> Result<()> { +async fn handle_export(path: &Path) -> Result<()> { let cache = ai_cache::AiCache::open()?; p::header("Exporting AI Cache"); @@ -303,7 +303,7 @@ async fn handle_export(path: &PathBuf) -> Result<()> { Ok(()) } -async fn handle_import(path: &PathBuf) -> Result<()> { +async fn handle_import(path: &Path) -> Result<()> { let mut cache = ai_cache::AiCache::open()?; p::header("Importing AI Cache"); diff --git a/src/commands/ai_chat.rs b/src/commands/ai_chat.rs index 76610510..90321d41 100644 --- a/src/commands/ai_chat.rs +++ b/src/commands/ai_chat.rs @@ -13,9 +13,8 @@ use crate::utils::{ }; use anyhow::{Context, Result}; use clap::Subcommand; -use rustyline::{DefaultEditor, Editor}; +use rustyline::DefaultEditor; use std::collections::HashMap; -use std::path::PathBuf; #[derive(Subcommand)] pub enum AiChatCommands { @@ -332,7 +331,7 @@ async fn generate_ai_response(prompt: &str, model: &str) -> Result { let response = ollama::generate_cached( model, - &prompt, + prompt, Some(opts), Some(ai_cache::DEFAULT_CACHE_TTL_SECONDS), "ask", diff --git a/src/commands/ai_deployment_test.rs b/src/commands/ai_deployment_test.rs index cead9411..5f30ea12 100644 --- a/src/commands/ai_deployment_test.rs +++ b/src/commands/ai_deployment_test.rs @@ -99,10 +99,7 @@ fn resolve_phases(value: &str) -> Result> { return Ok(vec![Phase::Pre, Phase::Post]); } - let phases: Vec = value - .split(',') - .filter_map(|part| Phase::parse(part)) - .collect(); + let phases: Vec = value.split(',').filter_map(Phase::parse).collect(); if phases.is_empty() { anyhow::bail!("Unknown phase '{}'. Use pre, post, or all", value); diff --git a/src/commands/ai_error.rs b/src/commands/ai_error.rs index 89e41291..502a75dd 100644 --- a/src/commands/ai_error.rs +++ b/src/commands/ai_error.rs @@ -3,10 +3,7 @@ //! Provides commands for managing AI error handling, viewing analytics, //! and configuring fallback providers. -use crate::utils::{ - ai_error_handler::{AiErrorHandler, ErrorAnalytics, ProviderConfig}, - print as p, -}; +use crate::utils::{ai_error_handler::AiErrorHandler, print as p}; use anyhow::Result; use clap::Subcommand; diff --git a/src/commands/ai_feedback.rs b/src/commands/ai_feedback.rs index 75398b37..01897cda 100644 --- a/src/commands/ai_feedback.rs +++ b/src/commands/ai_feedback.rs @@ -4,8 +4,6 @@ use crate::utils::print as p; use anyhow::{Context, Result}; use clap::{Args, Subcommand}; use colored::*; -use std::fs; -use std::path::PathBuf; #[derive(Subcommand)] pub enum AiFeedbackCommands { diff --git a/src/commands/ai_model_router.rs b/src/commands/ai_model_router.rs index 5dbb7093..251aca11 100644 --- a/src/commands/ai_model_router.rs +++ b/src/commands/ai_model_router.rs @@ -135,9 +135,15 @@ fn handle_classify(args: ClassifyArgs) -> Result<()> { p::kv("Complexity", &classification.complexity.to_string()); p::kv("Category", &classification.category.to_string()); p::kv("Est. tokens", &classification.estimated_tokens.to_string()); - p::kv("Requires reasoning", &classification.requires_reasoning.to_string()); + p::kv( + "Requires reasoning", + &classification.requires_reasoning.to_string(), + ); p::kv("Requires code", &classification.requires_code.to_string()); - p::kv("Confidence", &format!("{:.0}%", classification.confidence * 100.0)); + p::kv( + "Confidence", + &format!("{:.0}%", classification.confidence * 100.0), + ); if !classification.signals.is_empty() { p::kv("Signals", &classification.signals.join(", ")); } @@ -257,7 +263,15 @@ fn handle_stats(args: StatsArgs) -> Result<()> { p::info("No model performance data recorded yet."); p::info("Enable AI telemetry with: starforge ai-telemetry enable"); } else { - let headers = &["Provider", "Model", "Feature", "Calls", "Success %", "Avg ms", "Avg tokens"]; + let headers = &[ + "Provider", + "Model", + "Feature", + "Calls", + "Success %", + "Avg ms", + "Avg tokens", + ]; let rows: Vec> = stats .iter() .take(20) diff --git a/src/commands/ai_plan.rs b/src/commands/ai_plan.rs index 9b12f4d2..23f80b7f 100644 --- a/src/commands/ai_plan.rs +++ b/src/commands/ai_plan.rs @@ -172,7 +172,11 @@ fn handle_architecture(args: ArchitectureArgs) -> Result<()> { let archs = planner::suggest_architectures(&args.description); output_or_print(&archs, args.json, "Architecture Suggestions", |archs| { for arch in archs { - let marker = if arch.recommended { " ★ recommended" } else { "" }; + let marker = if arch.recommended { + " ★ recommended" + } else { + "" + }; println!(); println!(" {}{}", arch.name, marker); println!(" {}", arch.description); @@ -225,7 +229,10 @@ fn handle_timeline(args: TimelineArgs) -> Result<()> { p::kv("Total days", &t.total_days.to_string()); p::kv("Buffer days", &t.buffer_days.to_string()); p::kv("Start", &t.start_date.format("%Y-%m-%d").to_string()); - p::kv("Target completion", &t.target_completion.format("%Y-%m-%d").to_string()); + p::kv( + "Target completion", + &t.target_completion.format("%Y-%m-%d").to_string(), + ); println!(); p::info("Milestones:"); for m in &t.milestones { @@ -379,7 +386,10 @@ fn handle_show(args: ShowArgs) -> Result<()> { } fn print_plan_summary(plan: &planner::ProjectPlan) { - p::kv("Generated", &plan.generated_at.format("%Y-%m-%d %H:%M UTC").to_string()); + p::kv( + "Generated", + &plan.generated_at.format("%Y-%m-%d %H:%M UTC").to_string(), + ); p::kv("Tasks", &plan.tasks.len().to_string()); p::kv("Phases", &plan.phases.len().to_string()); p::kv("Risks", &plan.risks.len().to_string()); diff --git a/src/commands/ai_property_test.rs b/src/commands/ai_property_test.rs index 06d5c04c..d9e24c4d 100644 --- a/src/commands/ai_property_test.rs +++ b/src/commands/ai_property_test.rs @@ -321,7 +321,7 @@ fn handle_edge_cases(args: EdgeCasesArgs) -> Result<()> { .filter(|p| { p.target_function .as_ref() - .map_or(false, |f| args.functions.contains(f)) + .is_some_and(|f| args.functions.contains(f)) }) .collect() }; @@ -394,11 +394,14 @@ async fn handle_shrink(args: ShrinkArgs) -> Result<()> { confidence: 0.5, }; let prop = properties.first().unwrap_or(&default_prop); - let shrink = - apt::generate_test_cases(&[prop.clone()], &[], &apt::PropertyTestConfig::default()) - .first() - .and_then(|tc| tc.shrink_strategy.clone()) - .unwrap_or_else(|| "shrink numerics toward 0, strings toward empty".to_string()); + let shrink = apt::generate_test_cases( + std::slice::from_ref(prop), + &[], + &apt::PropertyTestConfig::default(), + ) + .first() + .and_then(|tc| tc.shrink_strategy.clone()) + .unwrap_or_else(|| "shrink numerics toward 0, strings toward empty".to_string()); match args.format.as_str() { "json" => { diff --git a/src/commands/ai_recommend.rs b/src/commands/ai_recommend.rs index b490e458..6195137e 100644 --- a/src/commands/ai_recommend.rs +++ b/src/commands/ai_recommend.rs @@ -564,7 +564,7 @@ fn find_rust_sources(dir: &PathBuf) -> Result> { { files.extend(find_rust_sources(&path)?); } - } else if path.extension().map_or(false, |e| e == "rs") { + } else if path.extension().is_some_and(|e| e == "rs") { files.push(path); } } diff --git a/src/commands/ai_search.rs b/src/commands/ai_search.rs index f405b253..c62629a7 100644 --- a/src/commands/ai_search.rs +++ b/src/commands/ai_search.rs @@ -4,7 +4,6 @@ use crate::utils::print as p; use anyhow::{Context, Result}; use clap::{Args, Subcommand}; use colored::*; -use std::fs; use std::path::PathBuf; #[derive(Subcommand)] diff --git a/src/commands/ai_test.rs b/src/commands/ai_test.rs index afdf3d56..c6de77a3 100644 --- a/src/commands/ai_test.rs +++ b/src/commands/ai_test.rs @@ -320,7 +320,7 @@ async fn generate_with_ai( ) -> Result { if !ollama::is_ollama_running().await { p::warn("Ollama is not running. Falling back to local generation."); - p::info(&ollama::cloud_fallback_message()); + p::info(ollama::cloud_fallback_message()); let analysis = ata::analyze_contract_for_testing(&request.contract_code)?; return generate_locally(request, &analysis); } @@ -452,10 +452,10 @@ fn test_{}_{}() {{ ) } -fn generate_setup_code(func: &ata::FunctionInfo, contract_name: &str) -> String { +fn generate_setup_code(func: &ata::FunctionInfo, _contract_name: &str) -> String { let mut lines = Vec::new(); - lines.push(format!("let contract_address = Address::random(&env);")); + lines.push("let contract_address = Address::random(&env);".to_string()); for param in &func.params { match param.param_type.as_str() { @@ -624,7 +624,7 @@ fn generate_warnings(analysis: &ata::ContractAnalysis) -> Vec { fn handle_generate_output( response: &ata::TestGenerationResponse, args: &GenerateArgs, - contract_name: &str, + _contract_name: &str, ) -> Result<()> { match args.format.as_str() { "json" => { @@ -1031,7 +1031,7 @@ fn handle_coverage(args: CoverageArgs) -> Result<()> { } }; - let prompt = ata::build_coverage_improvement_prompt(&ata::CoverageAnalysisRequest { + let _prompt = ata::build_coverage_improvement_prompt(&ata::CoverageAnalysisRequest { source_code: source_code.clone(), test_code: test_code.clone(), coverage_data: coverage_data.clone(), @@ -1104,7 +1104,7 @@ fn handle_coverage(args: CoverageArgs) -> Result<()> { } fn analyze_coverage_gaps( - source_code: &str, + _source_code: &str, _test_code: &str, coverage: &ata::CoverageInput, ) -> Vec { @@ -1205,7 +1205,7 @@ fn handle_maintain(args: MaintainArgs) -> Result<()> { t.contains(source_func) || source_func .strip_prefix("test_") - .map_or(false, |stripped| t.contains(stripped)) + .is_some_and(|stripped| t.contains(stripped)) }); if !has_test { @@ -1603,7 +1603,7 @@ async fn handle_test_data(args: TestDataArgs) -> Result<()> { Ok(()) } -fn generate_local_test_data(suggestions: &[ata::TestDataSuggestion], count: u32) -> String { +fn generate_local_test_data(suggestions: &[ata::TestDataSuggestion], _count: u32) -> String { let mut code = String::from( "// Generated by StarForge AI Test Assistant\n// Test data generators and edge cases\n\n", ); diff --git a/src/commands/ai_test_gen.rs b/src/commands/ai_test_gen.rs index 486e6e7c..80a3360f 100644 --- a/src/commands/ai_test_gen.rs +++ b/src/commands/ai_test_gen.rs @@ -104,6 +104,10 @@ pub async fn handle(cmd: AiTestGenCommands) -> Result<()> { } } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn handle_generate( file: PathBuf, output: PathBuf, @@ -264,7 +268,7 @@ async fn handle_reset_analytics() -> Result<()> { p::header("Reset Test Generation Analytics"); p::separator(); - let generator = AiTestGenerator::new(); + let _generator = AiTestGenerator::new(); // Note: This would require adding a reset method to AiTestGenerator // For now, just inform the user p::info("Analytics reset functionality would be implemented here."); diff --git a/src/commands/ai_tutorial_cmd.rs b/src/commands/ai_tutorial_cmd.rs index d6e6abdc..a95b9f9c 100644 --- a/src/commands/ai_tutorial_cmd.rs +++ b/src/commands/ai_tutorial_cmd.rs @@ -4,10 +4,10 @@ //! personalized learning paths, and progress tracking. use crate::utils::{ - ai_tutorial::{SkillLevel, StepResult, TutorialManager, TutorialTopic}, + ai_tutorial::{SkillLevel, TutorialManager}, print as p, }; -use anyhow::{Context, Result}; +use anyhow::Result; use clap::Subcommand; use dialoguer::{Confirm, Input, Select}; diff --git a/src/commands/analytics.rs b/src/commands/analytics.rs index cc6da67c..93ef288a 100644 --- a/src/commands/analytics.rs +++ b/src/commands/analytics.rs @@ -3,7 +3,6 @@ use anyhow::Result; use chrono::Utc; use clap::{Args, Subcommand}; use colored::Colorize; -use colored::*; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; @@ -646,7 +645,7 @@ fn calculate_health_score(success_rate: f64, recent_failures: usize, trend: &str _ => {} } - score.max(0.0).min(100.0) + score.clamp(0.0, 100.0) } /// Calculate health score for a contract @@ -689,7 +688,7 @@ pub fn calculate_contract_health( let performance_score = if !fees.is_empty() { let avg_fee = fees.iter().sum::() as f64 / fees.len() as f64; // Lower fees = better performance score (baseline is 5000 stroops) - ((10000.0 - avg_fee) / 10000.0 * 100.0).max(0.0).min(100.0) + ((10000.0 - avg_fee) / 10000.0 * 100.0).clamp(0.0, 100.0) } else { 50.0 }; diff --git a/src/commands/approval.rs b/src/commands/approval.rs index 92baf6a6..cdb5fafb 100644 --- a/src/commands/approval.rs +++ b/src/commands/approval.rs @@ -316,7 +316,7 @@ fn handle_show_workflow(args: ShowWorkflowArgs) -> Result<()> { p::kv("Active", if workflow.active { "yes" } else { "no" }); p::kv( "Created", - &workflow + workflow .created_at .get(..19) .unwrap_or(&workflow.created_at), @@ -522,14 +522,14 @@ fn handle_show_request(args: ShowRequestArgs) -> Result<()> { p::kv("Level progress", &request.level_progress()); p::kv( "Created", - &request.created_at.get(..19).unwrap_or(&request.created_at), + request.created_at.get(..19).unwrap_or(&request.created_at), ); p::kv( "Updated", - &request.updated_at.get(..19).unwrap_or(&request.updated_at), + request.updated_at.get(..19).unwrap_or(&request.updated_at), ); if let Some(ref expiry) = request.expires_at { - p::kv("Expires", &expiry.get(..19).unwrap_or(expiry)); + p::kv("Expires", expiry.get(..19).unwrap_or(expiry)); } if let Some(ref wf) = workflow { @@ -701,7 +701,7 @@ fn handle_dashboard() -> Result<()> { let summary = get_approval_summary()?; let requests = list_requests(None, None)?; - let workflows = list_workflows(true)?; + let _workflows = list_workflows(true)?; p::separator(); p::kv("Total requests", &summary.total_requests.to_string()); diff --git a/src/commands/autocomplete.rs b/src/commands/autocomplete.rs index b9924651..d30d5029 100644 --- a/src/commands/autocomplete.rs +++ b/src/commands/autocomplete.rs @@ -1,6 +1,4 @@ -use crate::utils::history::{ - history_file_path, load_history, prune_history, save_history, HistoryEntry, -}; +use crate::utils::history::{load_history, prune_history, save_history, HistoryEntry}; use anyhow::Result; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; diff --git a/src/commands/bridge.rs b/src/commands/bridge.rs index 3beb03eb..9c40dee9 100644 --- a/src/commands/bridge.rs +++ b/src/commands/bridge.rs @@ -7,7 +7,7 @@ use crate::utils::bridge::{ save_config, security::SecurityVerifier, state::StateSynchronizer, - BridgeConfig, BridgeTransferRecord, + BridgeTransferRecord, }; use crate::utils::print as p; use anyhow::Result; diff --git a/src/commands/collab.rs b/src/commands/collab.rs index e9161b2a..1fd06bdd 100644 --- a/src/commands/collab.rs +++ b/src/commands/collab.rs @@ -21,7 +21,7 @@ use chrono::{DateTime, Utc}; use clap::Subcommand; use serde::{Deserialize, Serialize}; use std::fs; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::Command; // ─── Sub-command enum ────────────────────────────────────────────────────── @@ -145,7 +145,7 @@ fn save_store(store: &CollabStore) -> Result<()> { Ok(()) } -fn record_review(file: &PathBuf, kind: &str) { +fn record_review(file: &Path, kind: &str) { if let Ok(mut store) = load_store() { store.reviews.push(ReviewRecord { file: file.display().to_string(), diff --git a/src/commands/compliance.rs b/src/commands/compliance.rs index bb8a93bd..1a0755f3 100644 --- a/src/commands/compliance.rs +++ b/src/commands/compliance.rs @@ -8,8 +8,6 @@ use crate::utils::print as p; use anyhow::Result; use clap::{Args, Subcommand}; use colored::Colorize; -use colored::*; -use std::collections::HashMap; #[derive(Subcommand)] pub enum ComplianceCommands { @@ -244,7 +242,7 @@ fn handle_check(args: CheckArgs) -> Result<()> { p::kv("Network", &report.network); p::kv( "Timestamp", - &report.timestamp.get(..19).unwrap_or(&report.timestamp), + report.timestamp.get(..19).unwrap_or(&report.timestamp), ); println!(); @@ -461,11 +459,11 @@ fn handle_show_policy(args: ShowPolicyArgs) -> Result<()> { p::kv("Enabled", if policy.enabled { "yes" } else { "no" }); p::kv( "Created", - &policy.created_at.get(..19).unwrap_or(&policy.created_at), + policy.created_at.get(..19).unwrap_or(&policy.created_at), ); p::kv( "Updated", - &policy.updated_at.get(..19).unwrap_or(&policy.updated_at), + policy.updated_at.get(..19).unwrap_or(&policy.updated_at), ); if !policy.config.is_empty() { @@ -579,7 +577,7 @@ fn handle_show_report(args: ShowReportArgs) -> Result<()> { p::kv("Network", &report.network); p::kv( "Timestamp", - &report.timestamp.get(..19).unwrap_or(&report.timestamp), + report.timestamp.get(..19).unwrap_or(&report.timestamp), ); let status_str = if report.all_passed { format!("{}", "PASSED".green()) diff --git a/src/commands/config.rs b/src/commands/config.rs index f969e7b8..ecf8ca93 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,7 +1,7 @@ use crate::utils::database; use crate::utils::{config, print as p}; use anyhow::Result; -use clap::{Args, Subcommand}; +use clap::Subcommand; #[derive(Subcommand)] pub enum ConfigCommands { @@ -327,6 +327,9 @@ fn show() -> Result<()> { Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn set_value(key: &str, value: &str) -> Result<()> { let mut cfg = config::load()?; match key { @@ -349,6 +352,9 @@ fn set_value(key: &str, value: &str) -> Result<()> { Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn parse_bool(value: &str) -> Result { match value.to_ascii_lowercase().as_str() { "true" | "1" | "yes" | "on" | "enabled" => Ok(true), @@ -360,6 +366,9 @@ fn parse_bool(value: &str) -> Result { } } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn plugin_trust(cmd: PluginTrustCommands) -> Result<()> { match cmd { PluginTrustCommands::List => { @@ -400,6 +409,9 @@ fn plugin_trust(cmd: PluginTrustCommands) -> Result<()> { Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn print_plugin_trust_sources(cfg: &config::Config) { p::header("Trusted Plugin Sources"); if cfg.plugin_trust.trusted_sources.is_empty() { diff --git a/src/commands/deploy.rs b/src/commands/deploy.rs index 25e7bb74..8b90c3a4 100644 --- a/src/commands/deploy.rs +++ b/src/commands/deploy.rs @@ -5,8 +5,8 @@ use crate::utils::{ self, last_successful, record_deployment, set_contract_id, set_duration, update_status, DeployRecord, DeployStatus, }, - deployment_monitor, horizon, notifications, optimizer, output, print as p, simulation_resources, - soroban, wallet_signer, + deployment_monitor, horizon, notifications, optimizer, output, print as p, + simulation_resources, soroban, wallet_signer, wasm_hash::{compute_wasm_hash, BuildEnvironment}, wasm_preflight, }; diff --git a/src/commands/deployment_automate.rs b/src/commands/deployment_automate.rs index 62ed12bd..e4528db5 100644 --- a/src/commands/deployment_automate.rs +++ b/src/commands/deployment_automate.rs @@ -138,6 +138,10 @@ pub async fn handle(cmd: DeploymentAutomateCommands) -> Result<()> { } } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn handle_run( wasm: PathBuf, network: String, @@ -459,7 +463,7 @@ fn print_automation_result(result: &crate::utils::deployment_automation::Complet } } -fn handle_history(limit: usize) -> Result<()> { +fn handle_history(_limit: usize) -> Result<()> { p::header("Deployment Automation History"); p::separator(); diff --git a/src/commands/deployment_optimize.rs b/src/commands/deployment_optimize.rs index e9aead10..894788e4 100644 --- a/src/commands/deployment_optimize.rs +++ b/src/commands/deployment_optimize.rs @@ -364,7 +364,7 @@ fn print_optimization_result( } } -fn handle_history(limit: usize) -> Result<()> { +fn handle_history(_limit: usize) -> Result<()> { p::header("Deployment Optimization History"); p::separator(); diff --git a/src/commands/deployments.rs b/src/commands/deployments.rs index d1bb2aba..8d6516b5 100644 --- a/src/commands/deployments.rs +++ b/src/commands/deployments.rs @@ -1,5 +1,5 @@ use crate::utils::deploy_history::{ - get_record, last_successful, load_history, set_verified, update_status, DeployStatus, + get_record, last_successful, load_history, set_verified, DeployStatus, }; use crate::utils::deployment_monitor; use crate::utils::deployment_monitoring_service::{ @@ -449,7 +449,7 @@ fn handle_monitor(args: MonitorArgs) -> Result<()> { // Populate tracker with history for visual status let records = load_history().unwrap_or_default(); - for (idx, r) in records.iter().enumerate().take(5) { + for (_idx, r) in records.iter().enumerate().take(5) { let tr_id = format!("dep-{}", &r.id[..8.min(r.id.len())]); tracker.start_tracking(&tr_id, &r.network, &r.wallet); if r.status == DeployStatus::Success { diff --git a/src/commands/docs.rs b/src/commands/docs.rs index 19b3d5c7..af968885 100644 --- a/src/commands/docs.rs +++ b/src/commands/docs.rs @@ -293,6 +293,10 @@ fn maintain( Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] fn generate( contract: String, name: Option, diff --git a/src/commands/feature_flags_cmd.rs b/src/commands/feature_flags_cmd.rs index b2a97dfa..0b977af9 100644 --- a/src/commands/feature_flags_cmd.rs +++ b/src/commands/feature_flags_cmd.rs @@ -8,7 +8,7 @@ //! - `enable ` / `disable ` //! - `rollout --percent N` – set the global rollout percentage //! - `segment add|remove|list` – manage segment rules (allow-list, %, -//! attribute predicate) +//! attribute predicate) //! - `variant add|remove|list` – manage A/B variants //! - `override set|clear|list` – per-user overrides //! - `metrics show|prune [--days]` @@ -21,9 +21,7 @@ use crate::utils::config; use crate::utils::database::Database; -use crate::utils::feature_flags::{ - self, FlagCategory, FlagManager, MetricKind, SegmentRule, UserContext, Variant, -}; +use crate::utils::feature_flags::{FlagCategory, FlagManager, SegmentRule, UserContext, Variant}; use crate::utils::print as p; use anyhow::{bail, Context, Result}; use clap::{Args, Subcommand}; @@ -312,6 +310,9 @@ pub async fn handle(args: FeatureFlagsArgs) -> Result<()> { // ── Helpers shared by subcommands ───────────────────────────────────────────── +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn hydrate_state(mgr: &FlagManager, db: &Database, flag_name: &str) -> Result<()> { if db.get_definition(flag_name)?.is_none() { bail!( diff --git a/src/commands/governance.rs b/src/commands/governance.rs index 82a51e7d..77c828cb 100644 --- a/src/commands/governance.rs +++ b/src/commands/governance.rs @@ -1,6 +1,4 @@ -use crate::utils::governance::{ - self, DashboardSummary, GovernanceConfig, GovernanceProposal, VoteChoice, -}; +use crate::utils::governance::{self, DashboardSummary, GovernanceProposal, VoteChoice}; use crate::utils::{config, confirmation, horizon, print as p}; use anyhow::Result; use clap::{Args, Subcommand}; diff --git a/src/commands/help.rs b/src/commands/help.rs index 5375753b..54e0ab67 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -172,7 +172,7 @@ async fn handle_command(cmd: &str, args: &HelpArgs) -> Result<()> { let canonical = cmd.trim().to_lowercase(); p::header(&format!("Help: {}", canonical)); let summary_line = - context_help::command_summary(&canonical).unwrap_or_else(|| help.description.as_str()); + context_help::command_summary(&canonical).unwrap_or(help.description.as_str()); println!(" {}", summary_line.dimmed()); println!(); @@ -338,12 +338,7 @@ fn expand_workflow(slug: &str) -> Result<()> { async fn handle_why(args: &HelpArgs) -> Result<()> { let error_text: Option = match args.error.clone() { Some(text) => Some(text), - None => match args.command.clone() { - // Treat `starforge help --why "some text"` (positional argument) - // as the error text too — it's the most ergonomic shape. - Some(text) => Some(text), - None => None, - }, + None => args.command.clone(), }; let error_text = match error_text { diff --git a/src/commands/migrate_ai.rs b/src/commands/migrate_ai.rs index ed091bea..150fea70 100644 --- a/src/commands/migrate_ai.rs +++ b/src/commands/migrate_ai.rs @@ -1,6 +1,6 @@ use crate::utils::migration_ai; use crate::utils::migration_ai::{AnalysisConfig, MigrationPlan}; -use crate::utils::{config, print as p}; +use crate::utils::print as p; use anyhow::{Context, Result}; use clap::{Args, Subcommand}; use colored::*; @@ -220,6 +220,10 @@ fn load_spec_entries( Ok(Vec::new()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] fn build_analysis_config( old_specs: &[String], new_specs: &[String], @@ -251,6 +255,10 @@ fn build_analysis_config( }) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] fn load_or_build_plan( old_wasm: Option<&PathBuf>, new_wasm: Option<&PathBuf>, @@ -404,7 +412,7 @@ fn handle_generate(args: GenerateArgs) -> Result<()> { let storage_changes = &plan.storage_changes; let contract_name = args.contract.unwrap_or_else(|| "Contract".to_string()); - let sdk_version = plan.to_version.clone(); + let _sdk_version = plan.to_version.clone(); let mut code = String::new(); code.push_str(&format!( @@ -527,7 +535,7 @@ fn handle_suggest(args: SuggestArgs) -> Result<()> { let priority_color = match suggestion.priority.as_str() { "high" => "HIGH".red().bold(), "medium" => "MEDIUM".yellow().bold(), - "low" | _ => "LOW".cyan(), + _ => "LOW".cyan(), }; println!( @@ -630,7 +638,7 @@ fn handle_plan(args: PlanArgs) -> Result<()> { } fn print_plan_summary(plan: &MigrationPlan) { - let compat_color = match plan.compatibility { + let _compat_color = match plan.compatibility { crate::utils::migration_ai::Compatibility::FullyCompatible => { "fully compatible".green().bold() } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index b1e4615d..8893ca02 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -13,8 +13,8 @@ pub mod ai_feedback; pub mod ai_ide; pub mod ai_model_router; pub mod ai_navigate; -pub mod ai_profile; pub mod ai_plan; +pub mod ai_profile; pub mod ai_property_test; pub mod ai_quality_gate; pub mod ai_recommend; @@ -67,8 +67,8 @@ pub mod multi_network; pub mod multisig_builder; pub mod mutate; pub mod network; -pub mod nl; pub mod new; +pub mod nl; pub mod node; pub mod optimize; pub mod orchestrate; diff --git a/src/commands/monitor.rs b/src/commands/monitor.rs index 6be48479..e54f64c1 100644 --- a/src/commands/monitor.rs +++ b/src/commands/monitor.rs @@ -8,7 +8,7 @@ use crate::utils::{ }; use anyhow::Result; use clap::Args; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, @@ -162,6 +162,10 @@ pub async fn handle(args: MonitorArgs) -> Result<()> { } } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn monitor_contract( contract_id: &str, events_filter: Option<&str>, @@ -312,10 +316,14 @@ async fn monitor_contract( Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] fn replay_contract_events( contract_id: &str, network: &str, - replay_path: &PathBuf, + replay_path: &Path, legacy_filter_set: &Option>, stream_filters: &EventStreamFilters, router: &EventRouter, @@ -323,7 +331,7 @@ fn replay_contract_events( triggers: &[EventTrigger], dashboard: bool, ) -> Result<()> { - let store = EventStore::new(replay_path.clone()); + let store = EventStore::new(replay_path.to_path_buf()); let events = store.replay()?; notifications::info(&format!( "Replaying {} persisted event(s) from {}.", @@ -365,6 +373,10 @@ fn replay_contract_events( Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] fn process_contract_event( network: &str, contract_id: &str, diff --git a/src/commands/multi_network.rs b/src/commands/multi_network.rs index 2a495d86..aca03439 100644 --- a/src/commands/multi_network.rs +++ b/src/commands/multi_network.rs @@ -3,7 +3,7 @@ //! Provides CLI commands for AI-driven multi-network deployment support. use crate::utils::{ - multi_network_deploy::{DeploymentStrategy, MultiNetworkConfig, MultiNetworkDeployer}, + multi_network_deploy::{DeploymentStrategy, MultiNetworkDeployer}, print as p, }; use anyhow::Result; @@ -322,7 +322,7 @@ fn print_deployment_result( println!(); } -fn handle_compare(include_custom: bool) -> Result<()> { +fn handle_compare(_include_custom: bool) -> Result<()> { p::header("Network Comparison"); p::separator(); diff --git a/src/commands/network.rs b/src/commands/network.rs index a9d0a067..8c3bf8cd 100644 --- a/src/commands/network.rs +++ b/src/commands/network.rs @@ -229,7 +229,7 @@ async fn test_network(network_name: Option) -> Result<()> { p::info(&format!("Testing connectivity to '{}'…", test_network)); p::info(&format!("Horizon: {}", net_cfg.horizon_url)); - let client = reqwest::Client::builder() + let _client = reqwest::Client::builder() .timeout(Duration::from_secs(10)) .pool_max_idle_per_host(10) .build()?; @@ -237,7 +237,7 @@ async fn test_network(network_name: Option) -> Result<()> { // Test Horizon endpoint let client = http_client::get_client(); match client - .get(&format!("{}/health", net_cfg.horizon_url)) + .get(format!("{}/health", net_cfg.horizon_url)) .send() .await { diff --git a/src/commands/new.rs b/src/commands/new.rs index 67af8f59..8bc3e957 100644 --- a/src/commands/new.rs +++ b/src/commands/new.rs @@ -201,6 +201,10 @@ async fn scaffold_contract_interactive(default_name: String) -> Result<()> { .await } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn scaffold_contract( name: String, template: String, @@ -879,14 +883,23 @@ fn dapp_index(name: &str) -> String { ) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn dapp_tsconfig() -> String { r#"{"compilerOptions": {"target": "es2020", "module": "esnext", "moduleResolution": "node", "esModuleInterop": true}}"#.to_string() } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn dapp_tsconfig_node() -> String { r#"{"extends": "./tsconfig.json", "compilerOptions": {"module": "commonjs", "target": "es2020", "moduleResolution": "node", "esModuleInterop": true}}"#.to_string() } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn dapp_vite_env_types(wallet_kit: bool) -> String { if wallet_kit { r#"interface ImportMetaEnv { VITE_NETWORK: string; VITE_WALLET_KIT: boolean; }"#.to_string() @@ -977,6 +990,9 @@ Source: `{source}` // ── Template Marketplace ────────────────────────────────────────────────────── +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] async fn handle_template_search(query: &str, tags: Option<&str>) -> Result<()> { p::header("Template Marketplace — Search"); p::kv("Query", query); @@ -1078,6 +1094,9 @@ impl Drop for PathCleanup { /// Run a single install step behind a spinner, finishing with a check mark on /// success or clearing the spinner and attaching an actionable message on /// failure. +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn install_step( label: &str, done: &str, @@ -1097,6 +1116,9 @@ fn install_step( } } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] async fn scaffold_from_marketplace(name: String, template_name: String) -> Result<()> { p::header(&format!("Scaffolding from Marketplace: {}", template_name)); @@ -1222,6 +1244,9 @@ async fn scaffold_from_marketplace(name: String, template_name: String) -> Resul Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn copy_template_contents(src: &Path, dst: &Path, project_name: &str) -> Result<()> { for entry in fs::read_dir(src)? { let entry = entry?; diff --git a/src/commands/nl.rs b/src/commands/nl.rs index 1c9ccc57..a58854ac 100644 --- a/src/commands/nl.rs +++ b/src/commands/nl.rs @@ -126,13 +126,15 @@ pub struct ExtractedEntities { pub keywords: Vec, } - // ── Pattern Definitions ──────────────────────────────────────────────────── struct Pattern { keywords: &'static [&'static str], intent_factory: fn(&ExtractedEntities) -> Intent, confidence: f64, + // Not currently called from any code path in this crate. Kept rather than + // removed since deleting it is a product decision, not a lint-scoping one. + #[allow(dead_code)] explanation: &'static str, } @@ -212,7 +214,8 @@ static PATTERNS: Lazy> = Lazy::new(|| { keywords: &["show", "network"], intent_factory: |_| Intent::ShowNetwork, confidence: 0.9, - explanation: "Shows the currently active network (testnet/mainnet) and its configuration.", + explanation: + "Shows the currently active network (testnet/mainnet) and its configuration.", }, Pattern { keywords: &["switch", "network"], @@ -287,14 +290,14 @@ static PATTERNS: Lazy> = Lazy::new(|| { const STOP_WORDS: &[&str] = &[ "a", "an", "the", "is", "are", "was", "were", "be", "been", "being", "have", "has", "had", "do", "does", "did", "will", "would", "shall", "should", "may", "might", "must", "can", - "could", "i", "you", "he", "she", "it", "we", "they", "me", "him", "her", "us", "them", - "my", "your", "his", "its", "our", "their", "this", "that", "these", "those", "am", "to", - "of", "in", "for", "on", "with", "at", "by", "from", "up", "about", "into", "through", - "during", "before", "after", "above", "below", "between", "out", "off", "over", "under", - "again", "further", "then", "once", "and", "but", "or", "nor", "not", "so", "very", "just", - "than", "too", "also", "here", "there", "when", "where", "why", "how", "all", "each", - "every", "both", "few", "more", "most", "other", "some", "such", "no", "only", "own", - "same", "now", "if", "please", "show", "me", "want", + "could", "i", "you", "he", "she", "it", "we", "they", "me", "him", "her", "us", "them", "my", + "your", "his", "its", "our", "their", "this", "that", "these", "those", "am", "to", "of", "in", + "for", "on", "with", "at", "by", "from", "up", "about", "into", "through", "during", "before", + "after", "above", "below", "between", "out", "off", "over", "under", "again", "further", + "then", "once", "and", "but", "or", "nor", "not", "so", "very", "just", "than", "too", "also", + "here", "there", "when", "where", "why", "how", "all", "each", "every", "both", "few", "more", + "most", "other", "some", "such", "no", "only", "own", "same", "now", "if", "please", "show", + "me", "want", ]; /// Extracts entities from the natural language input. @@ -306,14 +309,12 @@ fn extract_entities(input: &str) -> ExtractedEntities { // Extract wallet name (after "named", "called", "as", "name", or directly after "wallet") for i in 0..words.len() { - if matches!(words[i], "named" | "called" | "as" | "name") { - if i + 1 < words.len() { - let name = words[i + 1] - .trim_matches(|c: char| !c.is_alphanumeric() && c != '_' && c != '-'); - if !name.is_empty() { - entities.wallet_name = Some(name.to_string()); - break; - } + if matches!(words[i], "named" | "called" | "as" | "name") && i + 1 < words.len() { + let name = + words[i + 1].trim_matches(|c: char| !c.is_alphanumeric() && c != '_' && c != '-'); + if !name.is_empty() { + entities.wallet_name = Some(name.to_string()); + break; } } } @@ -370,33 +371,32 @@ fn extract_entities(input: &str) -> ExtractedEntities { // Extract amount (numbers after "fund", "send", etc.) for i in 0..words.len() { - if matches!(words[i], "fund" | "send" | "pay") { - if i + 1 < words.len() { - if words[i + 1].parse::().is_ok() { - entities.amount = Some(words[i + 1].to_string()); - break; - } - } + if matches!(words[i], "fund" | "send" | "pay") + && i + 1 < words.len() + && words[i + 1].parse::().is_ok() + { + entities.amount = Some(words[i + 1].to_string()); + break; } } // Extract function name (after "call", "run", "execute") for i in 0..words.len() { - if matches!(words[i], "call" | "run" | "execute" | "invoke") { - if i + 1 < words.len() { - let func = words[i + 1] - .trim_matches(|c: char| !c.is_alphanumeric() && c != '_'); - if !func.is_empty() { - entities.function_name = Some(func.to_string()); - break; - } + if matches!(words[i], "call" | "run" | "execute" | "invoke") && i + 1 < words.len() { + let func = words[i + 1].trim_matches(|c: char| !c.is_alphanumeric() && c != '_'); + if !func.is_empty() { + entities.function_name = Some(func.to_string()); + break; } } } // Collect meaningful keywords (exclude stop words) for word in &words { - let cleaned: String = word.chars().filter(|c| c.is_alphanumeric() || *c == '_').collect(); + let cleaned: String = word + .chars() + .filter(|c| c.is_alphanumeric() || *c == '_') + .collect(); if !cleaned.is_empty() && cleaned.len() > 2 && !STOP_WORDS.contains(&cleaned.as_str()) @@ -575,8 +575,7 @@ fn generate_explanation(intent: &Intent, command: &str) -> String { } Intent::ListWallets => { explanation.push_str("📋 I'll list all wallets saved locally.\n\n"); - explanation - .push_str("This shows wallet names, public keys, and networks.\n"); + explanation.push_str("This shows wallet names, public keys, and networks.\n"); } Intent::ShowWallet { name } => { explanation.push_str(&format!( @@ -592,8 +591,7 @@ fn generate_explanation(intent: &Intent, command: &str) -> String { "💰 I'll fund wallet '{}' via the testnet faucet.\n\n", name.as_deref().unwrap_or("wallet"), )); - explanation - .push_str("Friendbot sends 10,000 XLM to testnet accounts.\n"); + explanation.push_str("Friendbot sends 10,000 XLM to testnet accounts.\n"); } Intent::DeployContract { wallet, network } => { explanation.push_str("🚀 I'll deploy a compiled Soroban contract.\n\n"); @@ -626,23 +624,16 @@ fn generate_explanation(intent: &Intent, command: &str) -> String { ); } Intent::SwitchNetwork { network } => { - explanation.push_str(&format!( - "🔄 I'll switch to the {} network.\n\n", - network - )); - explanation - .push_str("This changes the active network for all subsequent commands.\n"); + explanation.push_str(&format!("🔄 I'll switch to the {} network.\n\n", network)); + explanation.push_str("This changes the active network for all subsequent commands.\n"); } Intent::StartNode => { - explanation - .push_str("🐳 I'll start a local Soroban devnet via Docker.\n\n"); + explanation.push_str("🐳 I'll start a local Soroban devnet via Docker.\n\n"); explanation.push_str("This launches a local Stellar node for testing.\n"); } Intent::RunDoctor => { explanation.push_str("🩺 I'll run diagnostics on your StarForge installation.\n\n"); - explanation.push_str( - "This checks for missing dependencies and connectivity issues.\n", - ); + explanation.push_str("This checks for missing dependencies and connectivity issues.\n"); } _ => { explanation.push_str("I'll execute the following command:\n\n"); @@ -728,10 +719,7 @@ pub async fn handle(args: NlArgs) -> Result<()> { println!(); p::kv("Input", input); p::kv("Intent", &format!("{:?}", intent)); - p::kv( - "Confidence", - &format!("{:.0}%", confidence * 100.0), - ); + p::kv("Confidence", &format!("{:.0}%", confidence * 100.0)); println!(); if !entities.keywords.is_empty() { @@ -807,9 +795,7 @@ pub async fn handle(args: NlArgs) -> Result<()> { println!(" {}. {}", i + 1, candidate.bright_white()); } println!(); - p::info( - "Please be more specific or use `starforge --help`.", - ); + p::info("Please be more specific or use `starforge --help`."); return Ok(()); } diff --git a/src/commands/optimize.rs b/src/commands/optimize.rs index f63db557..c5110c87 100644 --- a/src/commands/optimize.rs +++ b/src/commands/optimize.rs @@ -382,11 +382,12 @@ pub fn analyse_source(content: &str, file: &str) -> Vec { } // Suggest soroban_sdk::Vec instead of std::vec::Vec - if trimmed.contains("Vec<") && !trimmed.starts_with("//") { - if trimmed.contains("std::vec") - || (trimmed.contains("Vec<") && trimmed.contains("use std")) - { - suggestions.push(TransformSuggestion { + if trimmed.contains("Vec<") + && !trimmed.starts_with("//") + && (trimmed.contains("std::vec") + || (trimmed.contains("Vec<") && trimmed.contains("use std"))) + { + suggestions.push(TransformSuggestion { file: file.to_string(), line: line_no, category: TransformCategory::RedundantCode, @@ -394,7 +395,6 @@ pub fn analyse_source(content: &str, file: &str) -> Vec { suggested: line.replace("std::vec::Vec", "soroban_sdk::Vec").to_string(), reason: "Prefer soroban_sdk::Vec over std::vec::Vec in contract code for Soroban compatibility.".to_string(), }); - } } // Flag large string literals in contract code diff --git a/src/commands/perf.rs b/src/commands/perf.rs index e9dafc4b..03c33145 100644 --- a/src/commands/perf.rs +++ b/src/commands/perf.rs @@ -1,7 +1,6 @@ use crate::utils::{contract_profiler, performance as perf, print as p}; use anyhow::Result; use clap::Subcommand; -use std::collections::BTreeMap; use std::collections::HashMap; use std::path::PathBuf; diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 00cd85b2..58273a3c 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -363,7 +363,7 @@ fn load() -> Result<()> { return Ok(()); } - let config = config::load().unwrap_or_default(); + let _config = config::load().unwrap_or_default(); // Warn about any unknown-trust plugins before loading. for pl in reg.plugins.iter().filter(|p| { @@ -486,6 +486,9 @@ fn uninstall(name: String, purge: bool, yes: bool) -> Result<()> { Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn discover_commands_from_library(lib_path: &str) -> Result> { let path = Path::new(lib_path); let mut pm = PluginManager::new(); @@ -516,7 +519,7 @@ fn update(name: Option, yes: bool) -> Result<()> { return Ok(()); } - let config = config::load().unwrap_or_default(); + let _config = config::load().unwrap_or_default(); let to_update: Vec<_> = match &name { Some(n) => { @@ -720,7 +723,7 @@ fn verify(name: Option, deep: bool, runtime_check: bool) -> Result<()> { None => reg.plugins.iter().collect(), }; - let config = config::load().unwrap_or_default(); + let _config = config::load().unwrap_or_default(); let mut all_ok = true; for pl in &to_check { diff --git a/src/commands/privacy.rs b/src/commands/privacy.rs index 781ff2bc..c0b830d9 100644 --- a/src/commands/privacy.rs +++ b/src/commands/privacy.rs @@ -1,7 +1,6 @@ use crate::utils::{print as p, privacy}; use anyhow::Result; -use clap::{Args, Subcommand}; -use serde_json::json; +use clap::Subcommand; #[derive(Subcommand)] pub enum PrivacyCommands { diff --git a/src/commands/project.rs b/src/commands/project.rs index 5c4b211e..b67bfc01 100644 --- a/src/commands/project.rs +++ b/src/commands/project.rs @@ -9,15 +9,8 @@ //! - `risk` – Assess and mitigate project risks using AI analysis //! - `timeline` – Manage project timelines, milestones, and deadlines -use crate::utils::{config, ollama, print as p}; -use anyhow::{Context, Result}; -use chrono::{DateTime, Utc}; +use anyhow::Result; use clap::{Args, Subcommand}; -use colored::*; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::fs; -use std::path::PathBuf; // ─── Top-level Subcommand ────────────────────────────────────────── diff --git a/src/commands/recommend.rs b/src/commands/recommend.rs index 57c49d38..010b4576 100644 --- a/src/commands/recommend.rs +++ b/src/commands/recommend.rs @@ -46,7 +46,7 @@ pub async fn handle( .collect(); let skill_level = match skill.as_deref() { - Some(s) => match rec::SkillLevel::from_str(s) { + Some(s) => match rec::SkillLevel::parse_lenient(s) { Some(level) => level, None => { p::warn(&format!( diff --git a/src/commands/registry.rs b/src/commands/registry.rs index a45cdd2f..e48ee3b1 100644 --- a/src/commands/registry.rs +++ b/src/commands/registry.rs @@ -381,6 +381,10 @@ fn logout() -> Result<()> { Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn publish( path: PathBuf, name: Option, diff --git a/src/commands/security.rs b/src/commands/security.rs index b67c88a9..abaac1cb 100644 --- a/src/commands/security.rs +++ b/src/commands/security.rs @@ -10,7 +10,6 @@ use crate::utils::stream::{EventStreamFilters, SorobanEventStream}; use crate::utils::{config, notifications, soroban}; use anyhow::Result; use clap::{Args, Subcommand}; -use colored::Colorize; use std::fs; use std::path::PathBuf; use std::sync::{ @@ -692,12 +691,9 @@ fn handle_threat_detect(args: ThreatDetectArgs) -> Result<()> { p::kv("Malicious", &summary.malicious.to_string()); p::kv("Suspicious", &summary.suspicious.to_string()); - match args.format.as_str() { - "json" => { - let json = serde_json::to_string_pretty(&event)?; - println!("{}", json); - } - _ => {} + if args.format.as_str() == "json" { + let json = serde_json::to_string_pretty(&event)?; + println!("{}", json); } if event.classification == crate::utils::security::ThreatClassification::Malicious { diff --git a/src/commands/social.rs b/src/commands/social.rs index b158bca0..b74714a4 100644 --- a/src/commands/social.rs +++ b/src/commands/social.rs @@ -1,7 +1,6 @@ use crate::utils::{config, print as p, social}; use anyhow::Result; use clap::{Args, Subcommand}; -use std::path::PathBuf; #[derive(Subcommand)] pub enum SocialCommands { diff --git a/src/commands/template.rs b/src/commands/template.rs index adb9e29b..9f4645d9 100644 --- a/src/commands/template.rs +++ b/src/commands/template.rs @@ -1,9 +1,8 @@ use crate::utils::template_integration; use crate::utils::template_performance; -use crate::utils::{output, print as p, registry, template_customization_ai, templates}; +use crate::utils::{output, print as p, template_customization_ai, templates}; use anyhow::{Context, Result}; use clap::Subcommand; -use colored::Colorize; use std::path::PathBuf; #[derive(Subcommand)] @@ -274,6 +273,9 @@ pub async fn handle(cmd: TemplateCommands) -> Result<()> { } } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] async fn template_assist( template: String, project: PathBuf, @@ -322,6 +324,10 @@ async fn template_assist( } Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn import( path: PathBuf, name: Option, @@ -352,6 +358,10 @@ async fn import( Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn publish( path: PathBuf, name: Option, @@ -729,6 +739,9 @@ fn init() -> Result<()> { Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] async fn optimize(path: PathBuf, name: Option) -> Result<()> { let analysis = template_performance::analyze_template_directory(&path, name.as_deref())?; @@ -895,6 +908,9 @@ async fn info(name: String) -> Result<()> { Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] async fn fetch( source: String, name: Option, diff --git a/src/commands/template_security.rs b/src/commands/template_security.rs index 5a56bac5..a85e8960 100644 --- a/src/commands/template_security.rs +++ b/src/commands/template_security.rs @@ -155,7 +155,7 @@ async fn handle_scan( fn print_scan_result(result: &crate::utils::template_security_scanner::TemplateSecurityScanResult) { // Overall score - let score_color = if result.security_score >= 80.0 { + let _score_color = if result.security_score >= 80.0 { "green" } else if result.security_score >= 60.0 { "yellow" @@ -284,7 +284,7 @@ fn print_scan_result(result: &crate::utils::template_security_scanner::TemplateS } } -fn handle_history(limit: usize) -> Result<()> { +fn handle_history(_limit: usize) -> Result<()> { p::header("Security Scan History"); p::separator(); diff --git a/src/commands/test.rs b/src/commands/test.rs index 141726b0..113d4436 100644 --- a/src/commands/test.rs +++ b/src/commands/test.rs @@ -1,6 +1,6 @@ use crate::utils::{ config, contract_testing, print as p, rollback_testing, test_automation, test_coverage, - test_generator, test_runner, + test_runner, }; use anyhow::Result; use clap::Args; @@ -496,7 +496,7 @@ pub async fn handle(args: TestArgs) -> Result<()> { } } - let timings: Vec = report + let _timings: Vec = report .results .iter() .map(|r| crate::utils::test_optimizer::TestCaseTiming { diff --git a/src/commands/upgrade_auto.rs b/src/commands/upgrade_auto.rs index 84ee760c..700b15e7 100644 --- a/src/commands/upgrade_auto.rs +++ b/src/commands/upgrade_auto.rs @@ -501,22 +501,20 @@ pub fn analyse_compat( let old_spec = decode_spec_model(old_bytes); let new_spec = decode_spec_model(new_bytes); - match (&old_spec, &new_spec) { - (Err(err), _) => issues.push(CompatIssue { + if let (Err(err), _) = (&old_spec, &new_spec) { + issues.push(CompatIssue { kind: "old-abi-metadata-missing".to_string(), severity: "warning".to_string(), description: format!("Unable to decode old contract ABI metadata: {err}"), - }), - _ => {} + }) } - match (&old_spec, &new_spec) { - (_, Err(err)) => issues.push(CompatIssue { + if let (_, Err(err)) = (&old_spec, &new_spec) { + issues.push(CompatIssue { kind: "new-abi-metadata-missing".to_string(), severity: "warning".to_string(), description: format!("Unable to decode new contract ABI metadata: {err}"), - }), - _ => {} + }) } let abi = match (&old_spec, &new_spec) { diff --git a/src/commands/wallet.rs b/src/commands/wallet.rs index 9901c10d..81ec0edd 100644 --- a/src/commands/wallet.rs +++ b/src/commands/wallet.rs @@ -8,8 +8,7 @@ use clap::Subcommand; use colored::*; use ed25519_dalek::{Signer, SigningKey}; use rand::RngCore; -use serde::{Deserialize, Serialize}; -use std::collections::HashSet; +use serde::Serialize; use std::fs; use std::path::PathBuf; use stellar_strkey::ed25519::{PrivateKey as StellarPrivateKey, PublicKey as StellarPublicKey}; @@ -584,6 +583,10 @@ fn prompt_recovery_phrase() -> Result { Ok(phrase) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn create( name: String, fund: bool, @@ -1170,6 +1173,10 @@ fn rename(old_name: String, new_name: String) -> Result<()> { Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn rotate_wallet( name: String, fund: bool, @@ -1315,6 +1322,9 @@ async fn rotate_wallet( Ok(()) } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn wallet_history(name: String, reveal: bool) -> Result<()> { config::validate_wallet_name(&name)?; let cfg = config::load()?; @@ -1460,6 +1470,10 @@ fn export_wallet(name_opt: Option, all: bool, output: PathBuf, strict: b Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] fn import_wallet( name: Option, file: Option, diff --git a/src/lib.rs b/src/lib.rs index df34f074..94695b33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -#![allow(dead_code, unused, clippy::all)] - pub mod commands; pub mod plugins; pub mod utils; diff --git a/src/main.rs b/src/main.rs index 4100713c..5b4f859a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,3 @@ -#![allow(dead_code, unused, clippy::all)] - pub use starforge::commands; pub mod curation; pub use starforge::plugins; @@ -814,7 +812,7 @@ fn handle_external_plugin(args: Vec) -> anyhow::Result<()> { let plugin_name = &args[0]; let plugin_args = &args[1..]; - let cfg = starforge::utils::config::load()?; + let _cfg = starforge::utils::config::load()?; let reg = plugins::registry::load_registry().unwrap_or_default(); if reg.plugins.is_empty() { anyhow::bail!( diff --git a/src/plugins/loader.rs b/src/plugins/loader.rs index c9c07f29..0d6f470d 100644 --- a/src/plugins/loader.rs +++ b/src/plugins/loader.rs @@ -6,9 +6,8 @@ use crate::plugins::interface::{ use crate::plugins::manifest; use crate::plugins::registry::{load_registry, TrustLevel}; use anyhow::Result; -use libloading::{Library, Symbol}; +use libloading::Library; use std::collections::HashMap; -use std::ffi::OsStr; use std::path::Path; use std::rc::Rc; @@ -335,7 +334,7 @@ impl PluginManager { if let Some((plugin, _)) = self.plugins.get(name) { plugin.execute(args) } else { - return Err(format!("Plugin '{}' not found", name)); + Err(format!("Plugin '{}' not found", name)) } } } diff --git a/src/plugins/registry.rs b/src/plugins/registry.rs index 7d2f14d4..bcc30557 100644 --- a/src/plugins/registry.rs +++ b/src/plugins/registry.rs @@ -1,5 +1,4 @@ -use crate::plugins::manifest; -use crate::utils::config::{self, Config}; +use crate::utils::config::Config; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::fs; @@ -306,7 +305,7 @@ pub fn install_plugin( source: &str, starforge_version: &str, plugin_version: &str, - description: &str, + _description: &str, commands: Vec, ) -> Result<()> { if !library_path.exists() { diff --git a/src/utils/ai.rs b/src/utils/ai.rs index 99094e39..43fe8dce 100644 --- a/src/utils/ai.rs +++ b/src/utils/ai.rs @@ -436,8 +436,11 @@ impl AIService for OllamaAdapter { } } +/// Registered AI service backends, keyed by provider. +type ProviderMap = RwLock>>>>; + pub struct AIServiceManager { - providers: RwLock>>>>, + providers: ProviderMap, circuit_breakers: RwLock>>>, fallback_order: Vec, provider_models: HashMap, diff --git a/src/utils/ai_accessibility.rs b/src/utils/ai_accessibility.rs index 1704449f..1e07f156 100644 --- a/src/utils/ai_accessibility.rs +++ b/src/utils/ai_accessibility.rs @@ -451,10 +451,7 @@ pub fn screen_reader_format(text: &str, cfg: &AccessibilityConfig) -> String { let mut output = String::new(); if cfg.verbose_descriptions { - output.push_str(&format!( - "Document with {} lines. ", - lines.len() - )); + output.push_str(&format!("Document with {} lines. ", lines.len())); } for (i, line) in lines.iter().enumerate() { @@ -464,9 +461,17 @@ pub fn screen_reader_format(text: &str, cfg: &AccessibilityConfig) -> String { } if trimmed.starts_with("✓") || trimmed.starts_with("Success") { - output.push_str(&format!("Success notification, line {}: {}. ", i + 1, trimmed)); + output.push_str(&format!( + "Success notification, line {}: {}. ", + i + 1, + trimmed + )); } else if trimmed.starts_with("✗") || trimmed.starts_with("Error") { - output.push_str(&format!("Error notification, line {}: {}. ", i + 1, trimmed)); + output.push_str(&format!( + "Error notification, line {}: {}. ", + i + 1, + trimmed + )); } else if trimmed.starts_with("⚠") || trimmed.starts_with("Warning") { output.push_str(&format!("Warning, line {}: {}. ", i + 1, trimmed)); } else if trimmed.starts_with("→") { @@ -523,7 +528,11 @@ pub fn simplify_text_local(text: &str) -> String { let mut result = text.to_string(); for (from, to) in replacements { result = result.replace(from, to); - let capitalized = format!("{}{}", from.chars().next().unwrap().to_uppercase(), &from[1..]); + let capitalized = format!( + "{}{}", + from.chars().next().unwrap().to_uppercase(), + &from[1..] + ); let to_cap = format!("{}{}", to.chars().next().unwrap().to_uppercase(), &to[1..]); result = result.replace(&capitalized, &to_cap); } @@ -709,9 +718,7 @@ pub fn format_output(text: &str, cfg: &AccessibilityConfig) -> String { pub fn voice_commands_by_category() -> HashMap> { let mut map: HashMap> = HashMap::new(); for cmd in voice_commands() { - map.entry(cmd.category.clone()) - .or_default() - .push(cmd); + map.entry(cmd.category.clone()).or_default().push(cmd); } map } diff --git a/src/utils/ai_cache.rs b/src/utils/ai_cache.rs index 34a31b75..9517db28 100644 --- a/src/utils/ai_cache.rs +++ b/src/utils/ai_cache.rs @@ -12,14 +12,12 @@ //! - Support for cache prewarming //! - Manual cache invalidation commands -use crate::utils::database::{db_path, Database}; -use anyhow::{Context, Result}; -use chrono::{DateTime, Utc}; -use rusqlite::{params, Connection, OptionalExtension}; +use crate::utils::database::Database; +use anyhow::Result; +use rusqlite::{params, OptionalExtension}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; -use std::path::PathBuf; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::{SystemTime, UNIX_EPOCH}; /// Default TTL for cached AI responses (7 days) pub const DEFAULT_CACHE_TTL_SECONDS: u64 = 7 * 24 * 60 * 60; diff --git a/src/utils/ai_context.rs b/src/utils/ai_context.rs index 8c4047bb..eac2511e 100644 --- a/src/utils/ai_context.rs +++ b/src/utils/ai_context.rs @@ -2,7 +2,6 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::{Path, PathBuf}; -use std::sync::Arc; use tokio::sync::RwLock; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -249,7 +248,7 @@ impl AIContextManager { if path.is_dir() && path .file_name() - .map_or(false, |n| n == "contracts" || n == "src") + .is_some_and(|n| n == "contracts" || n == "src") { if let Ok(contract_items) = collect_rust_files_sync(&path, &self.config) { items.extend(contract_items); @@ -391,7 +390,7 @@ fn collect_rust_files_sync( let entry = entry?; let path = entry.path(); - if path.is_file() && path.extension().map_or(false, |e| e == "rs") { + if path.is_file() && path.extension().is_some_and(|e| e == "rs") { if let Ok(metadata) = std::fs::metadata(&path) { if metadata.len() <= config.max_file_size_bytes { if let Ok(content) = std::fs::read_to_string(&path) { diff --git a/src/utils/ai_conversation.rs b/src/utils/ai_conversation.rs index c3a07f81..257c1d93 100644 --- a/src/utils/ai_conversation.rs +++ b/src/utils/ai_conversation.rs @@ -207,15 +207,9 @@ impl ConversationManager { // Trim context if too large if context.messages.len() > self.max_context_messages { - let remove_count = context.messages.len() - self.max_context_messages; + let _remove_count = context.messages.len() - self.max_context_messages; // Keep system messages, remove oldest user/assistant messages - context.messages.retain(|m| { - if m.role == MessageRole::System { - true - } else { - false - } - }); + context.messages.retain(|m| m.role == MessageRole::System); // Add back recent messages up to limit let recent_messages: Vec<_> = context @@ -371,20 +365,17 @@ impl ConversationManager { // Workflow-based suggestions if let Some(workflow) = &context.workflow_state { - match workflow.workflow_type { - WorkflowType::ContractDeployment => { - if !workflow.completed_steps.contains(&"compile".to_string()) { - suggestions.push(Suggestion { - title: "Compile contract".to_string(), - description: "Build the WASM file".to_string(), - action_type: SuggestionAction::Command( - "cargo build --target wasm32-unknown-unknown --release".to_string(), - ), - confidence: 0.95, - }); - } - } - _ => {} + if workflow.workflow_type == WorkflowType::ContractDeployment + && !workflow.completed_steps.contains(&"compile".to_string()) + { + suggestions.push(Suggestion { + title: "Compile contract".to_string(), + description: "Build the WASM file".to_string(), + action_type: SuggestionAction::Command( + "cargo build --target wasm32-unknown-unknown --release".to_string(), + ), + confidence: 0.95, + }); } } diff --git a/src/utils/ai_deployment_planner.rs b/src/utils/ai_deployment_planner.rs index a222f7b9..b3ef6726 100644 --- a/src/utils/ai_deployment_planner.rs +++ b/src/utils/ai_deployment_planner.rs @@ -206,6 +206,10 @@ pub enum PlanStatus { // ─── Planner Implementation ────────────────────────────────────────────────── +// `target_network`/`max_gas_price` are not currently read from any code path +// in this crate. Kept rather than removed since deleting them is a product +// decision, not a lint-scoping one. +#[allow(dead_code)] pub struct AiDeploymentPlanner { contract_path: PathBuf, target_network: String, @@ -311,7 +315,7 @@ Contract code: num_ctx: Some(8192), }; - let response = ollama::generate(&self.model, &prompt, Some(opts)) + let _response = ollama::generate(&self.model, &prompt, Some(opts)) .await .context("AI contract analysis failed")?; @@ -411,7 +415,7 @@ Contract code: upgrade_patterns, security_findings, optimization_suggestions, - readiness_score: score.max(0).min(100) as u8, + readiness_score: score.clamp(0, 100) as u8, }) } @@ -524,7 +528,7 @@ Contract code: // Suggest next weekday at 8 AM UTC let mut start_time = now; let days_to_add = 1; - start_time = start_time + chrono::Duration::days(days_to_add); + start_time += chrono::Duration::days(days_to_add); start_time = start_time .with_hour(8) .unwrap() @@ -536,9 +540,9 @@ Contract code: // If weekend, move to Monday let weekday = start_time.weekday(); if weekday == chrono::Weekday::Sat { - start_time = start_time + chrono::Duration::days(2); + start_time += chrono::Duration::days(2); } else if weekday == chrono::Weekday::Sun { - start_time = start_time + chrono::Duration::days(1); + start_time += chrono::Duration::days(1); } let end_time = start_time + chrono::Duration::hours(4); @@ -659,7 +663,7 @@ Contract code: Ok(RiskAssessment { overall, - score: score.max(0).min(100) as u8, + score: score.clamp(0, 100) as u8, categories, mitigations, }) @@ -668,7 +672,7 @@ Contract code: async fn create_rollback_plan( &self, analysis: &ContractAnalysis, - network: &NetworkRecommendation, + _network: &NetworkRecommendation, ) -> Result { let mut steps = Vec::new(); diff --git a/src/utils/ai_doc_qa.rs b/src/utils/ai_doc_qa.rs index 442758f0..0ac39a78 100644 --- a/src/utils/ai_doc_qa.rs +++ b/src/utils/ai_doc_qa.rs @@ -427,35 +427,34 @@ pub fn analyze_question(question: &str) -> QuestionAnalysis { let lower = question.to_lowercase(); let tokens = tokenize(question); - let intent = if lower.starts_with("how") - || lower.starts_with("what do i") - || lower.contains("steps to") - { - QuestionIntent::HowTo - } else if lower.starts_with("what is") - || lower.starts_with("what are") - || lower.starts_with("what's") - { - QuestionIntent::WhatIs - } else if lower.contains("error") - || lower.contains("fail") - || lower.contains("not work") - || lower.contains("fix") - || lower.contains("problem") - || lower.contains("issue") - { - QuestionIntent::Troubleshooting - } else if lower.starts_with("why") || lower.contains("reason") { - QuestionIntent::Why - } else if lower.contains(" vs ") - || lower.contains("difference") - || lower.contains("compare") - || lower.contains("better") - { - QuestionIntent::Comparison - } else { - QuestionIntent::General - }; + let intent = + if lower.starts_with("how") || lower.starts_with("what do i") || lower.contains("steps to") + { + QuestionIntent::HowTo + } else if lower.starts_with("what is") + || lower.starts_with("what are") + || lower.starts_with("what's") + { + QuestionIntent::WhatIs + } else if lower.contains("error") + || lower.contains("fail") + || lower.contains("not work") + || lower.contains("fix") + || lower.contains("problem") + || lower.contains("issue") + { + QuestionIntent::Troubleshooting + } else if lower.starts_with("why") || lower.contains("reason") { + QuestionIntent::Why + } else if lower.contains(" vs ") + || lower.contains("difference") + || lower.contains("compare") + || lower.contains("better") + { + QuestionIntent::Comparison + } else { + QuestionIntent::General + }; let mut topics = Vec::new(); for (domain, keywords) in TOPIC_INDEX { @@ -650,7 +649,7 @@ fn chunk_text(content: &str, chunk_size: usize, overlap: usize) -> Vec { if content.is_empty() { return vec![]; } - let step = chunk_size.saturating_sub(overlap).max(1); + let _step = chunk_size.saturating_sub(overlap).max(1); let mut chunks = Vec::new(); let mut start = 0usize; while start < content.len() { @@ -1111,7 +1110,7 @@ impl DocQaEngine { } let analysis = analyze_question(question); - let answer_language = language.unwrap_or_else(|| analysis.language); + let answer_language = language.unwrap_or(analysis.language); let tokens = analysis.tokens.clone(); let mut hits = self.index.retrieve(&tokens, 6, 1.0); @@ -1179,7 +1178,7 @@ impl DocQaEngine { language: answer_language, confidence: estimate_confidence(&hits, &analysis), mode: AnswerMode::Generated, - follow_up_suggestions: follow_up_suggestions(&question, &analysis), + follow_up_suggestions: follow_up_suggestions(question, &analysis), latency_ms: started.elapsed().as_millis(), }, Err(_) => extractive_answer(question, &hits, answer_language, started), diff --git a/src/utils/ai_docs.rs b/src/utils/ai_docs.rs index a1202962..f1c7a450 100644 --- a/src/utils/ai_docs.rs +++ b/src/utils/ai_docs.rs @@ -277,6 +277,10 @@ fn build_function_docs(extracted: &ExtractedDocs, languages: &[DocLanguage]) -> .collect() } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] fn build_sections( extracted: &ExtractedDocs, source_text: &str, diff --git a/src/utils/ai_error_handler.rs b/src/utils/ai_error_handler.rs index ed8f52de..7fcf4776 100644 --- a/src/utils/ai_error_handler.rs +++ b/src/utils/ai_error_handler.rs @@ -3,7 +3,7 @@ //! Provides robust error handling for AI operations with automatic recovery, //! fallback mechanisms, and user-friendly error messages. -use anyhow::{Context, Result}; +use anyhow::Result; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -160,7 +160,7 @@ impl ProviderConfig { } /// Error analytics tracker -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct ErrorAnalytics { pub total_errors: u64, pub errors_by_category: HashMap, @@ -169,18 +169,6 @@ pub struct ErrorAnalytics { pub failed_recoveries: u64, } -impl Default for ErrorAnalytics { - fn default() -> Self { - ErrorAnalytics { - total_errors: 0, - errors_by_category: HashMap::new(), - errors_by_provider: HashMap::new(), - successful_recoveries: 0, - failed_recoveries: 0, - } - } -} - impl ErrorAnalytics { pub fn record_error(&mut self, error: &AiError) { self.total_errors += 1; diff --git a/src/utils/ai_feedback.rs b/src/utils/ai_feedback.rs index 35008925..40c67ccd 100644 --- a/src/utils/ai_feedback.rs +++ b/src/utils/ai_feedback.rs @@ -12,7 +12,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use crate::utils::config; @@ -249,7 +249,7 @@ pub fn learn_preferences(store: &mut FeedbackStore) { CorrectionCategory::Performance => PreferenceType::PerformancePriority, _ => continue, }; - let map = pref_counts.entry(pref_type).or_insert_with(HashMap::new); + let map = pref_counts.entry(pref_type).or_default(); *map.entry(correction.corrected_output.clone()).or_insert(0) += 1; } } diff --git a/src/utils/ai_gas_estimation.rs b/src/utils/ai_gas_estimation.rs index bcd24d3c..9d0324ec 100644 --- a/src/utils/ai_gas_estimation.rs +++ b/src/utils/ai_gas_estimation.rs @@ -32,6 +32,9 @@ pub struct AiGasHistoryEntry { } pub struct AiGasEstimator { + // Not currently called from any code path in this crate. Kept rather than + // removed since deleting it is a product decision, not a lint-scoping one. + #[allow(dead_code)] model_version: String, } diff --git a/src/utils/ai_model_router.rs b/src/utils/ai_model_router.rs index 9774e563..72f84501 100644 --- a/src/utils/ai_model_router.rs +++ b/src/utils/ai_model_router.rs @@ -292,7 +292,10 @@ pub fn classify_task(prompt: &str, category_hint: Option) -> TaskC || lower.contains("compare") || lower.contains("trade-off") || lower.contains("risk") - || matches!(category, TaskCategory::Planning | TaskCategory::SecurityAudit); + || matches!( + category, + TaskCategory::Planning | TaskCategory::SecurityAudit + ); if requires_reasoning { signals.push("reasoning_keywords".into()); @@ -301,19 +304,19 @@ pub fn classify_task(prompt: &str, category_hint: Option) -> TaskC signals.push("contains_code".into()); } - let complexity = if word_count > 800 || line_count > 60 || requires_reasoning && word_count > 300 - { - signals.push("high_token_count".into()); - TaskComplexity::Expert - } else if word_count > 300 || line_count > 25 || requires_reasoning { - TaskComplexity::Complex - } else if word_count > 80 || requires_code { - TaskComplexity::Moderate - } else { - TaskComplexity::Simple - }; + let complexity = + if word_count > 800 || line_count > 60 || requires_reasoning && word_count > 300 { + signals.push("high_token_count".into()); + TaskComplexity::Expert + } else if word_count > 300 || line_count > 25 || requires_reasoning { + TaskComplexity::Complex + } else if word_count > 80 || requires_code { + TaskComplexity::Moderate + } else { + TaskComplexity::Simple + }; - let estimated_tokens = (word_count as u32 * 2).max(256).min(8192); + let estimated_tokens = (word_count as u32 * 2).clamp(256, 8192); let confidence = if category_hint.is_some() { 0.95 } else if signals.len() >= 2 { @@ -362,8 +365,7 @@ fn infer_category(lower: &str, has_code: bool, signals: &mut Vec) -> Tas TaskCategory::Optimization } else if lower.contains("document") || lower.contains("readme") || lower.contains("explain") { TaskCategory::Documentation - } else if lower.contains("generate") || lower.contains("implement") || lower.contains("write") - { + } else if lower.contains("generate") || lower.contains("implement") || lower.contains("write") { if has_code { signals.push("code_generation".into()); TaskCategory::CodeGeneration @@ -438,7 +440,11 @@ pub async fn route_task( if prefs.prefer_local && ollama_available { if let Some(local) = candidates.iter().find(|m| m.is_local) { - return Ok(build_decision(local, &classification, "Local Ollama preferred by user")); + return Ok(build_decision( + local, + &classification, + "Local Ollama preferred by user", + )); } } @@ -458,12 +464,12 @@ pub async fn route_task( score_b.cmp(&score_a) }); - let best = candidates.first().context("No suitable model found for task")?; + let best = candidates + .first() + .context("No suitable model found for task")?; let reason = match (classification.complexity, classification.category) { - (TaskComplexity::Simple, _) if prefs.cost_sensitive => { - "Simple task — optimizing for cost" - } + (TaskComplexity::Simple, _) if prefs.cost_sensitive => "Simple task — optimizing for cost", (_, TaskCategory::CodeGeneration) => "Code generation — code-specialized model", (_, TaskCategory::SecurityAudit) => "Security audit — high-capability model", (TaskComplexity::Expert, _) => "Expert complexity — capable model selected", @@ -524,7 +530,7 @@ fn build_decision( None } else { ai_telemetry::estimate_cost( - &provider_name(&model.provider), + provider_name(&model.provider), &model.model, classification.estimated_tokens as u64, (classification.estimated_tokens / 2) as u64, @@ -573,12 +579,7 @@ pub fn config_from_decision(decision: &RoutingDecision) -> AIServiceConfig { providers.insert(decision.provider.clone(), provider_config); let fallback_order = std::iter::once(decision.provider.clone()) - .chain( - decision - .fallback_chain - .iter() - .map(|(p, _)| p.clone()), - ) + .chain(decision.fallback_chain.iter().map(|(p, _)| p.clone())) .collect(); AIServiceConfig { @@ -590,10 +591,15 @@ pub fn config_from_decision(decision: &RoutingDecision) -> AIServiceConfig { } } +/// (provider, model, feature) +type ModelKey = (String, String, String); +/// (call_count, success_count, total_latency_ms, total_tokens) +type ModelTotals = (u64, u64, u64, u64); + /// Aggregate model performance from local AI telemetry records. pub fn model_performance_stats(days: Option) -> Result> { let records = ai_telemetry::load_records(days)?; - let mut by_model: HashMap<(String, String, String), (u64, u64, u64, u64)> = HashMap::new(); + let mut by_model: HashMap = HashMap::new(); for r in &records { let key = (r.provider.clone(), r.model.clone(), r.feature.clone()); @@ -609,21 +615,23 @@ pub fn model_performance_stats(days: Option) -> Result = by_model .into_iter() - .map(|((provider, model, feature), (total, success, latency, tokens))| { - ModelPerformanceRecord { - provider, - model, - feature, - success_rate: if total > 0 { - success as f64 / total as f64 - } else { - 0.0 - }, - avg_latency_ms: if total > 0 { latency / total } else { 0 }, - avg_tokens: if total > 0 { tokens / total } else { 0 }, - total_calls: total, - } - }) + .map( + |((provider, model, feature), (total, success, latency, tokens))| { + ModelPerformanceRecord { + provider, + model, + feature, + success_rate: if total > 0 { + success as f64 / total as f64 + } else { + 0.0 + }, + avg_latency_ms: if total > 0 { latency / total } else { 0 }, + avg_tokens: if total > 0 { tokens / total } else { 0 }, + total_calls: total, + } + }, + ) .collect(); stats.sort_by(|a, b| b.total_calls.cmp(&a.total_calls)); diff --git a/src/utils/ai_project_planner.rs b/src/utils/ai_project_planner.rs index 12e7cdd3..972800e0 100644 --- a/src/utils/ai_project_planner.rs +++ b/src/utils/ai_project_planner.rs @@ -308,7 +308,8 @@ pub fn suggest_architectures(description: &str) -> Vec { { architectures.push(ArchitectureSuggestion { name: "Modular Multi-Contract".into(), - description: "Separate contracts for distinct domains with cross-contract calls.".into(), + description: "Separate contracts for distinct domains with cross-contract calls." + .into(), contract_modules: vec![ ContractModule { name: "core".into(), @@ -359,7 +360,10 @@ pub fn breakdown_tasks(description: &str, phases: &[DevelopmentPhase]) -> Vec Vec Vec Vec Vec Tim let milestones = phases .iter() .scan(start, |cursor, phase| { - *cursor = *cursor + Duration::days(phase.estimated_days as i64); + *cursor += Duration::days(phase.estimated_days as i64); Some(Milestone { name: phase.name.clone(), date: *cursor, @@ -607,7 +623,8 @@ pub fn identify_risks(description: &str) -> Vec { category: RiskCategory::Technical, severity: RiskSeverity::High, likelihood: RiskLikelihood::Possible, - mitigation: "Profile with starforge ai profile; optimize storage access patterns".into(), + mitigation: "Profile with starforge ai profile; optimize storage access patterns" + .into(), contingency: "Refactor hot paths and redeploy".into(), }, ProjectRisk { @@ -691,7 +708,8 @@ pub fn default_deployment_plan() -> DeploymentPlan { "Initialize contract state".into(), "Verify on-chain deployment".into(), ], - rollback_procedure: "Keep previous contract ID; redirect clients; migrate state if upgradeable".into(), + rollback_procedure: + "Keep previous contract ID; redirect clients; migrate state if upgradeable".into(), monitoring_setup: vec![ "Contract event monitoring".into(), "Gas usage alerts".into(), diff --git a/src/utils/ai_property_testing.rs b/src/utils/ai_property_testing.rs index 63de29ba..0d9c4370 100644 --- a/src/utils/ai_property_testing.rs +++ b/src/utils/ai_property_testing.rs @@ -9,8 +9,6 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::Path; use crate::utils::ai_test_assistant as ata; @@ -385,7 +383,7 @@ fn generate_test_code_for_property( include_shrink: bool, ) -> String { let shrink_section = if include_shrink { - format!("\n // Shrink strategy: minimize counterexample to smallest failing input") + "\n // Shrink strategy: minimize counterexample to smallest failing input".to_string() } else { String::new() }; @@ -395,7 +393,7 @@ fn generate_test_code_for_property( .filter(|inv| { prop.target_function .as_ref() - .map_or(false, |f| inv.functions_affected.contains(f)) + .is_some_and(|f| inv.functions_affected.contains(f)) }) .map(|inv| format!(" // Invariant: {} — {}", inv.name, inv.expression)) .collect(); diff --git a/src/utils/ai_recommendations.rs b/src/utils/ai_recommendations.rs index 7aff6787..7a372bda 100644 --- a/src/utils/ai_recommendations.rs +++ b/src/utils/ai_recommendations.rs @@ -6,11 +6,9 @@ //! - Priority-ranked recommendations //! - Implementation guidance for each recommendation -use anyhow::{Context, Result}; +use anyhow::Result; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::fs; -use std::path::Path; use crate::utils::ai_test_assistant as ata; diff --git a/src/utils/ai_refactor.rs b/src/utils/ai_refactor.rs index fcb9fbee..9e3e3563 100644 --- a/src/utils/ai_refactor.rs +++ b/src/utils/ai_refactor.rs @@ -353,7 +353,7 @@ pub async fn handle(cmd: RefactorCommands) -> Result<()> { handle_refactor( &file, &model, - &name.as_deref().unwrap_or("extracted"), + name.as_deref().unwrap_or("extracted"), TaskType::ExtractFunction, output, ) @@ -491,10 +491,7 @@ async fn handle_refactor( let session_id = format!( "refactor-{}-{}", Utc::now().format("%Y%m%d-%H%M%S"), - sha256::hash(&refactored) - .chars() - .take(8) - .collect::() + sha256::hash(refactored).chars().take(8).collect::() ); // Save session for tracking/rollback diff --git a/src/utils/ai_search.rs b/src/utils/ai_search.rs index b14dd8b1..30615065 100644 --- a/src/utils/ai_search.rs +++ b/src/utils/ai_search.rs @@ -6,14 +6,11 @@ //! - Pattern discovery and similar code finding //! - Usage example generation and recommendation -use anyhow::{Context, Result}; +use anyhow::Result; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; -use crate::utils::pattern_library::{self, PatternCategory}; - // ── Types ──────────────────────────────────────────────────────────────────── /// A code search result with relevance scoring. @@ -129,6 +126,9 @@ struct IndexEntry { content: String, tokens: Vec, is_test: bool, + // Not currently called from any code path in this crate. Kept rather than + // removed since deleting it is a product decision, not a lint-scoping one. + #[allow(dead_code)] is_contract: bool, } @@ -185,7 +185,7 @@ fn find_rust_files(dir: &Path) -> Result> { { files.extend(find_rust_files(&path)?); } - } else if path.extension().map_or(false, |e| e == "rs") { + } else if path.extension().is_some_and(|e| e == "rs") { files.push(path); } } diff --git a/src/utils/ai_telemetry.rs b/src/utils/ai_telemetry.rs index 287fd97e..7eacb308 100644 --- a/src/utils/ai_telemetry.rs +++ b/src/utils/ai_telemetry.rs @@ -104,12 +104,7 @@ fn price_per_1k_tokens(provider: &str, model: &str) -> Option<(f64, f64)> { } /// Estimate USD cost for a call given provider, model, and token counts. -pub fn estimate_cost( - provider: &str, - model: &str, - tokens_in: u64, - tokens_out: u64, -) -> Option { +pub fn estimate_cost(provider: &str, model: &str, tokens_in: u64, tokens_out: u64) -> Option { estimate_cost_usd(provider, model, Some(tokens_in), Some(tokens_out)) } diff --git a/src/utils/ai_test_analytics.rs b/src/utils/ai_test_analytics.rs index 158a5177..123a39f9 100644 --- a/src/utils/ai_test_analytics.rs +++ b/src/utils/ai_test_analytics.rs @@ -66,6 +66,12 @@ pub struct TestAnalyticsService { analytics: Arc>, } +impl Default for TestAnalyticsService { + fn default() -> Self { + Self::new() + } +} + impl TestAnalyticsService { pub fn new() -> Self { TestAnalyticsService { diff --git a/src/utils/ai_test_assistant.rs b/src/utils/ai_test_assistant.rs index bfa37d6b..56fce382 100644 --- a/src/utils/ai_test_assistant.rs +++ b/src/utils/ai_test_assistant.rs @@ -1,6 +1,5 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; @@ -448,8 +447,8 @@ fn parse_function_line(line: &str, line_num: u32) -> Option { Some(FunctionInfo { name, signature: line.trim().to_string(), - is_public: is_public, - is_entry_point: is_entry_point, + is_public, + is_entry_point, is_mutating, params, return_type, @@ -519,9 +518,7 @@ pub fn generate_test_priorities(analysis: &ContractAnalysis) -> Vec 3 { - TestPriority::High - } else if func.complexity_score > 5 { + } else if func.complexity_score > 5 || (func.is_mutating && func.complexity_score > 3) { TestPriority::High } else if func.is_mutating { TestPriority::Medium @@ -1095,7 +1092,7 @@ pub fn find_test_files(project_path: &Path) -> Vec { if let Ok(entries) = fs::read_dir(&tests_dir) { for entry in entries.flatten() { let path = entry.path(); - if path.extension().map_or(false, |ext| ext == "rs") { + if path.extension().is_some_and(|ext| ext == "rs") { test_files.push(path); } } @@ -1108,7 +1105,7 @@ pub fn find_test_files(project_path: &Path) -> Vec { if let Ok(entries) = fs::read_dir(&src_dir) { for entry in entries.flatten() { let path = entry.path(); - if path.extension().map_or(false, |ext| ext == "rs") { + if path.extension().is_some_and(|ext| ext == "rs") { if let Ok(content) = fs::read_to_string(&path) { if content.contains("#[cfg(test)]") { test_files.push(path); diff --git a/src/utils/ai_test_generator.rs b/src/utils/ai_test_generator.rs index 9ee7f4f3..59185351 100644 --- a/src/utils/ai_test_generator.rs +++ b/src/utils/ai_test_generator.rs @@ -7,7 +7,7 @@ use anyhow::{Context, Result}; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use tokio::sync::RwLock; @@ -239,11 +239,7 @@ impl AiTestGenerator { } /// Generate comprehensive test suite - pub async fn generate_test_suite( - &self, - target_file: &PathBuf, - code: &str, - ) -> Result { + pub async fn generate_test_suite(&self, target_file: &Path, code: &str) -> Result { let start_time = std::time::Instant::now(); let analysis = self.analyze_code(code)?; @@ -302,7 +298,7 @@ impl AiTestGenerator { "{}_tests", target_file.file_stem().unwrap().to_string_lossy() ), - target_file: target_file.clone(), + target_file: target_file.to_path_buf(), tests, coverage_estimate, generated_at: Utc::now(), @@ -646,7 +642,7 @@ fn test_{}_regression() {{ "// Estimated coverage: {:.1}%\n", suite.coverage_estimate * 100.0 )); - output.push_str("\n"); + output.push('\n'); for test in &suite.tests { output.push_str(&format!("// {}\n", test.description)); @@ -655,7 +651,7 @@ fn test_{}_regression() {{ test.test_type, test.category )); output.push_str(&test.code); - output.push_str("\n"); + output.push('\n'); } std::fs::write(output_path, output).context("Failed to write test suite file")?; diff --git a/src/utils/ai_tutorial.rs b/src/utils/ai_tutorial.rs index 7dfa2f45..4709eb00 100644 --- a/src/utils/ai_tutorial.rs +++ b/src/utils/ai_tutorial.rs @@ -9,7 +9,6 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; use tokio::sync::RwLock; -use uuid::Uuid; /// User skill level assessment #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -339,10 +338,11 @@ impl TutorialManager { for topic in learning_path { for tutorial in tutorials.values() { - if tutorial.topic == topic && !progress.completed_tutorials.contains(&tutorial.id) { - if tutorial.difficulty.clone() as i32 <= skill_level.clone() as i32 + 1 { - recommended.push(tutorial.clone()); - } + if tutorial.topic == topic + && !progress.completed_tutorials.contains(&tutorial.id) + && tutorial.difficulty.clone() as i32 <= skill_level.clone() as i32 + 1 + { + recommended.push(tutorial.clone()); } } } @@ -496,7 +496,7 @@ impl TutorialManager { answer .parse::() .ok() - .map_or(false, |idx| idx < options.len()) + .is_some_and(|idx| idx < options.len()) } } ExerciseType::CommandExecution => { diff --git a/src/utils/ai_validation.rs b/src/utils/ai_validation.rs index 55b6f347..0967b27c 100644 --- a/src/utils/ai_validation.rs +++ b/src/utils/ai_validation.rs @@ -1,6 +1,5 @@ -use anyhow::{Context, Result}; +use anyhow::Result; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ValidationResult { diff --git a/src/utils/approval_engine.rs b/src/utils/approval_engine.rs index 92848cd0..e29129c2 100644 --- a/src/utils/approval_engine.rs +++ b/src/utils/approval_engine.rs @@ -223,6 +223,10 @@ pub fn deactivate_workflow(id: &str) -> Result<()> { } } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] pub fn create_request( workflow_id: &str, contract_id: &str, diff --git a/src/utils/audit.rs b/src/utils/audit.rs index 6acf0ed2..a2d6cc94 100644 --- a/src/utils/audit.rs +++ b/src/utils/audit.rs @@ -133,12 +133,12 @@ pub fn get_audit_report(start_time: Option<&str>, end_time: Option<&str>) -> Res .iter() .filter(|e| { if let Some(start) = start_time { - if e.timestamp < start.to_string() { + if e.timestamp.as_str() < start { return false; } } if let Some(end) = end_time { - if e.timestamp > end.to_string() { + if e.timestamp.as_str() > end { return false; } } @@ -189,6 +189,10 @@ pub fn export_audit_log_csv(entries: &[AuditEntry]) -> String { csv } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] pub fn log_approval_action( action: &str, actor: &str, diff --git a/src/utils/bindings.rs b/src/utils/bindings.rs index c90a0b1f..cdc2f160 100644 --- a/src/utils/bindings.rs +++ b/src/utils/bindings.rs @@ -3,7 +3,7 @@ use std::io::Cursor; use std::path::Path; use stellar_xdr::curr::{ Limited, Limits, ReadXdr, ScSpecEntry, ScSpecFunctionV0, ScSpecTypeDef, ScSpecUdtEnumV0, - ScSpecUdtStructV0, ScSpecUdtUnionV0, + ScSpecUdtStructV0, }; #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -337,7 +337,13 @@ fn generate_rust(metadata: &ContractMetadata) -> String { let params = function .inputs .iter() - .map(|input| format!("{}: {}", sanitize_ident(&input.name), rust_type(&input.type_name))) + .map(|input| { + format!( + "{}: {}", + sanitize_ident(&input.name), + rust_type(&input.type_name) + ) + }) .collect::>() .join(", "); let return_type = function @@ -346,7 +352,7 @@ fn generate_rust(metadata: &ContractMetadata) -> String { .map(rust_type) .unwrap_or_else(|| "()".to_string()); let comma = if params.is_empty() { "" } else { ", " }; - + out.push_str(&format!( "\tpub fn {rust_name}(&self{comma}{params}) -> Result<{return_type}> {{\n\ \t\tlet mut cmd = Command::new(\"starforge\");\n\ @@ -385,7 +391,7 @@ fn generate_rust(metadata: &ContractMetadata) -> String { \t{\n\ \t\tresult.parse().context(\"Failed to parse result\")\n\ \t}\n\n\ - }\n\n" + }\n\n", ); for struct_def in &metadata.structs { @@ -741,9 +747,12 @@ fn rust_type(type_name: &str) -> String { "I256" => "String".to_string(), _ => { // Handle complex types like Option, Result, Vec, etc. - if type_name.starts_with("Option<") || type_name.starts_with("Result<") || - type_name.starts_with("Vec<") || type_name.starts_with("Map<") || - type_name.starts_with("BytesN<") { + if type_name.starts_with("Option<") + || type_name.starts_with("Result<") + || type_name.starts_with("Vec<") + || type_name.starts_with("Map<") + || type_name.starts_with("BytesN<") + { type_name.to_string() } else { // Assume it's a custom type @@ -766,12 +775,12 @@ fn ts_type(type_name: &str) -> String { _ => { // Handle complex types if type_name.starts_with("Option<") { - let inner = &type_name[7..type_name.len()-1]; // Remove "Option<>" + let inner = &type_name[7..type_name.len() - 1]; // Remove "Option<>" format!("{} | null", ts_type(inner)) } else if type_name.starts_with("Result<") { "any".to_string() } else if type_name.starts_with("Vec<") { - let inner = &type_name[4..type_name.len()-1]; // Remove "Vec<>" + let inner = &type_name[4..type_name.len() - 1]; // Remove "Vec<>" format!("Array<{}>", ts_type(inner)) } else if type_name.starts_with("Map<") { "Record".to_string() @@ -801,12 +810,12 @@ fn python_type(type_name: &str) -> String { _ => { // Handle complex types if type_name.starts_with("Option<") { - let inner = &type_name[7..type_name.len()-1]; // Remove "Option<>" + let inner = &type_name[7..type_name.len() - 1]; // Remove "Option<>" format!("Optional[{}]", python_type(inner)) } else if type_name.starts_with("Result<") { "Any".to_string() } else if type_name.starts_with("Vec<") { - let inner = &type_name[4..type_name.len()-1]; // Remove "Vec<>" + let inner = &type_name[4..type_name.len() - 1]; // Remove "Vec<>" format!("List[{}]", python_type(inner)) } else if type_name.starts_with("Map<") { "Dict[str, Any]".to_string() @@ -842,12 +851,12 @@ fn go_type(type_name: &str) -> String { // Handle complex types if type_name.starts_with("Option<") { // In Go, we can use pointer types for optional - let inner = &type_name[7..type_name.len()-1]; // Remove "Option<>" + let inner = &type_name[7..type_name.len() - 1]; // Remove "Option<>" format!("*{}", go_type(inner)) } else if type_name.starts_with("Result<") { "interface{}".to_string() } else if type_name.starts_with("Vec<") { - let inner = &type_name[4..type_name.len()-1]; // Remove "Vec<>" + let inner = &type_name[4..type_name.len() - 1]; // Remove "Vec<>" format!("[]{}", go_type(inner)) } else if type_name.starts_with("Map<") { "map[string]interface{}".to_string() diff --git a/src/utils/bridge/providers.rs b/src/utils/bridge/providers.rs index 5a35a857..71b09496 100644 --- a/src/utils/bridge/providers.rs +++ b/src/utils/bridge/providers.rs @@ -81,7 +81,7 @@ pub fn default_providers() -> Vec { /// Initiate a cross-chain transfer through the configured provider. pub fn initiate_transfer( - provider: &BridgeProvider, + _provider: &BridgeProvider, request: &BridgeTransferRequest, ) -> anyhow::Result { let transfer_id = uuid::Uuid::new_v4().to_string(); diff --git a/src/utils/compliance.rs b/src/utils/compliance.rs index a9c92326..a22062d5 100644 --- a/src/utils/compliance.rs +++ b/src/utils/compliance.rs @@ -401,7 +401,7 @@ pub fn run_compliance_checks( policy_name: policy.name.clone(), passed: regulatory_checks.iter().all(|r| r.passed), severity: policy.severity.clone(), - message: format!("Regulatory compliance check complete"), + message: "Regulatory compliance check complete".to_string(), } } PolicyType::SecurityCompliance => { @@ -852,7 +852,7 @@ fn check_gdpr_compliance(network: &str, _contract_id: &str) -> Vec Vec { +fn check_soc2_compliance(_network: &str, _contract_id: &str) -> Vec { vec![ RegulatoryCheck { framework: RegulatoryFramework::Soc2, @@ -1477,7 +1477,7 @@ pub fn export_report_csv(report: &ComplianceReport) -> String { csv_escape("policy"), csv_escape(&check.policy_id), csv_escape(&check.policy_name), - csv_escape(&check.passed), + csv_escape(check.passed), csv_escape(&check.severity), csv_escape(&check.message), )); @@ -1488,7 +1488,7 @@ pub fn export_report_csv(report: &ComplianceReport) -> String { csv_escape("regulatory"), csv_escape(""), csv_escape(&check.requirement), - csv_escape(&check.passed), + csv_escape(check.passed), csv_escape(&check.severity), csv_escape(&check.message), csv_escape(&check.framework), @@ -1500,7 +1500,7 @@ pub fn export_report_csv(report: &ComplianceReport) -> String { csv_escape("best_practice"), csv_escape(""), csv_escape(&practice.check), - csv_escape(&practice.passed), + csv_escape(practice.passed), csv_escape(&practice.severity), csv_escape(&practice.recommendation), csv_escape(&practice.category), @@ -1517,6 +1517,9 @@ pub fn export_report_json(report: &ComplianceReport) -> Result { // Default policy initialization // ──────────────────────────────────────────────── +// Eight multi-line `create_policy(...)?` calls read far more clearly as +// sequential pushes than as one giant `vec![]` literal. +#[allow(clippy::vec_init_then_push)] pub fn build_default_policies() -> Result> { let existing = load_policies_raw()?; if !existing.is_empty() { diff --git a/src/utils/context_help.rs b/src/utils/context_help.rs index e5817f36..a66739e8 100644 --- a/src/utils/context_help.rs +++ b/src/utils/context_help.rs @@ -29,9 +29,10 @@ use crate::utils::history::HistoryEntry; // ── Public types ────────────────────────────────────────────────────────────── /// A coarse expertise tier used to tune tip verbosity. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Expertise { /// Few or no prior commands in this area — keep tips short and concrete. + #[default] Beginner, /// Some prior commands — surface intermediate tips and best practices. Intermediate, @@ -39,12 +40,6 @@ pub enum Expertise { Advanced, } -impl Default for Expertise { - fn default() -> Self { - Expertise::Beginner - } -} - impl Expertise { /// Lower-case label for printing in headers. pub fn label(self) -> &'static str { @@ -78,13 +73,13 @@ pub struct HelpContext<'a> { impl<'a> HelpContext<'a> { /// True when category `cat` should be considered enabled. pub fn category_enabled(&self, cat: &str) -> bool { - if self.disabled_categories.iter().any(|c| *c == cat) { + if self.disabled_categories.contains(&cat) { return false; } if self.enabled_categories.is_empty() { true } else { - self.enabled_categories.iter().any(|c| *c == cat) + self.enabled_categories.contains(&cat) } } } @@ -393,7 +388,7 @@ pub const PROACTIVE_BLOCKLIST: &[&str] = &["help", "info", "completions", "versi /// worth saying OR if the command is on the [[PROACTIVE_BLOCKLIST]]. pub fn proactive_tip(command: &str, history: &[HistoryEntry]) -> Option { let cmd = command.trim().to_lowercase(); - if PROACTIVE_BLOCKLIST.iter().any(|c| *c == cmd.as_str()) { + if PROACTIVE_BLOCKLIST.contains(&cmd.as_str()) { return None; } diff --git a/src/utils/contract_assertions.rs b/src/utils/contract_assertions.rs index dfb5488d..e73df54d 100644 --- a/src/utils/contract_assertions.rs +++ b/src/utils/contract_assertions.rs @@ -197,7 +197,7 @@ pub fn assert_storage_numeric( let actual = match actual_val .as_i64() .map(i128::from) - .or_else(|| actual_val.as_u64().map(|u| i128::from(u))) + .or_else(|| actual_val.as_u64().map(i128::from)) { Some(n) => n, None => { diff --git a/src/utils/contract_deps.rs b/src/utils/contract_deps.rs index 9d50cf8f..d6b634f7 100644 --- a/src/utils/contract_deps.rs +++ b/src/utils/contract_deps.rs @@ -3,7 +3,7 @@ use semver::VersionReq; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ContractDependencies { diff --git a/src/utils/contract_health_monitor.rs b/src/utils/contract_health_monitor.rs index 16a1da1f..88309fa1 100644 --- a/src/utils/contract_health_monitor.rs +++ b/src/utils/contract_health_monitor.rs @@ -135,7 +135,7 @@ fn run_health_probes(contract_id: &str, network: &str) -> Vec { .unwrap_or_default() .into_iter() .filter(|r| r.contract_id.as_deref() == Some(contract_id) && r.network == network) - .last(); + .next_back(); let (deploy_status, deploy_msg) = match &last_deploy { Some(r) if r.status == deploy_history::DeployStatus::Success => ( ContractHealthStatus::Healthy, diff --git a/src/utils/contract_test_framework.rs b/src/utils/contract_test_framework.rs index 2587fb66..655f5c4b 100644 --- a/src/utils/contract_test_framework.rs +++ b/src/utils/contract_test_framework.rs @@ -1,20 +1,16 @@ use crate::utils::{ contract_assertions::{ - assert_balance_eq, assert_error_contains, assert_event_emitted, assert_event_not_emitted, - assert_ok, assert_return_value, assert_storage_eq, AssertionResult, AssertionStatus, - AssertionSuite, ContractAssertions, + assert_error_contains, assert_return_value, AssertionSuite, ContractAssertions, }, - contract_fixtures::{ContractFixture, FixtureContext, FixtureRegistry}, + contract_fixtures::{ContractFixture, FixtureContext}, contract_mocks::{MockAddress, MockContractClient, MockEnvironment, StorageKey}, contract_test_runner::{ContractTestRunner, TestRunConfig, TestRunSummary}, testnet_integration::{ - run_connectivity_smoke_test, SorobanNetwork, TestnetConfig, TestnetSession, - TestnetTestReport, TestnetTestResult, + run_connectivity_smoke_test, TestnetConfig, TestnetSession, TestnetTestReport, }, }; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; use std::time::Instant; @@ -220,10 +216,8 @@ impl FrameworkTestSuite { let suite_start = Instant::now(); // Setup fixture - let fixture_ctx: Option = self - .fixture - .as_mut() - .and_then(|f| f.setup().ok().map(|ctx| ctx.clone())); + let fixture_ctx: Option = + self.fixture.as_mut().and_then(|f| f.setup().ok().cloned()); let mut results = Vec::new(); for case in &self.cases { @@ -231,7 +225,7 @@ impl FrameworkTestSuite { // Seed environment from fixture context if let Some(ref ctx) = fixture_ctx { - for (key, seed) in &ctx.storage { + for seed in ctx.storage.values() { env.storage.set( StorageKey { scope: format!("{:?}", seed.durability).to_lowercase(), @@ -240,7 +234,7 @@ impl FrameworkTestSuite { seed.value.clone(), ); } - for (_, account) in &ctx.accounts { + for account in ctx.accounts.values() { env.auth .auto_approve(MockAddress::new(account.address.clone())); } @@ -613,7 +607,7 @@ fn render_junit_report(result: &FrameworkRunResult) -> String { /// Standard test cases for any counter-style contract. pub fn counter_test_suite() -> FrameworkTestSuite { use crate::utils::contract_fixtures::counter_fixture; - use crate::utils::contract_mocks::{counter_env, MockAddress}; + use crate::utils::contract_mocks::MockAddress; let mut suite = FrameworkTestSuite::new("counter").with_fixture(counter_fixture()); diff --git a/src/utils/cost_management.rs b/src/utils/cost_management.rs index b4b1f313..67eb8e93 100644 --- a/src/utils/cost_management.rs +++ b/src/utils/cost_management.rs @@ -202,7 +202,7 @@ pub fn budget_status(network: Option<&str>) -> Result> { let history = ce::load_cost_history()?; Ok(budgets .iter() - .filter(|b| network.map_or(true, |n| b.network == n)) + .filter(|b| network.is_none_or(|n| b.network == n)) .map(|b| budget_status_for(b, &history)) .collect()) } @@ -459,7 +459,7 @@ pub fn generate_cost_report_from( ) -> CostReport { let filtered: Vec<&CostHistoryEntry> = history .iter() - .filter(|e| network.map_or(true, |n| e.estimate.network == n)) + .filter(|e| network.is_none_or(|n| e.estimate.network == n)) .collect(); if filtered.is_empty() { diff --git a/src/utils/crypto.rs b/src/utils/crypto.rs index 1bb0901b..054411b4 100644 --- a/src/utils/crypto.rs +++ b/src/utils/crypto.rs @@ -298,7 +298,10 @@ fn argon2_from_params(params: &Params) -> Argon2<'_> { Argon2::from(params.clone()) } -fn parse_encrypted_bundle(bundle: &str) -> Result<(Vec, Vec, Vec, Option)> { +/// (salt, nonce, ciphertext, KDF params if the bundle encodes non-default ones) +type EncryptedBundle = (Vec, Vec, Vec, Option); + +fn parse_encrypted_bundle(bundle: &str) -> Result { let parts: Vec<&str> = bundle.split(':').collect(); match parts.len() { 3 => { diff --git a/src/utils/database.rs b/src/utils/database.rs index 07598df2..f7b6b408 100644 --- a/src/utils/database.rs +++ b/src/utils/database.rs @@ -1,9 +1,8 @@ use anyhow::{Context, Result}; use rusqlite::{params, Connection, OptionalExtension}; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; -use std::sync::Arc; use sha2::{Digest, Sha256}; +use std::path::PathBuf; pub fn db_path() -> PathBuf { crate::utils::config::config_dir().join("starforge.db") @@ -16,10 +15,10 @@ pub const CURRENT_SCHEMA_VERSION: i64 = 1; pub trait Migration: Send + Sync { /// Version number for this migration (must be unique) fn version(&self) -> i64; - + /// Description of what this migration does fn description(&self) -> &str; - + /// Apply the migration (upgrade) fn up(&self, conn: &Connection) -> Result<()>; @@ -45,33 +44,30 @@ pub struct MigrationResult { } /// Error types for migration operations -#[derive(Debug)] +#[derive(Debug, thiserror::Error)] pub enum MigrationError { + #[error("Migration version {0} is already applied")] AlreadyApplied(i64), + + #[error("Migration version {0} not found")] NotFound(i64), + + #[error("Cannot rollback: no migrations applied")] NothingToRollback, + + #[error("Migration version {0} depends on unapplied version {1}")] MissingDependency(i64, i64), + + #[error("Invalid migration sequence: versions must be consecutive")] InvalidSequence, + + #[error("Database schema version {0} is not supported (minimum: {1}, maximum: {2})")] UnsupportedVersion(i64, i64, i64), - MigrationFailed(String), -} -impl std::fmt::Display for MigrationError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::AlreadyApplied(v) => write!(f, "Migration version {} is already applied", v), - Self::NotFound(v) => write!(f, "Migration version {} not found", v), - Self::NothingToRollback => write!(f, "Cannot rollback: no migrations applied"), - Self::MissingDependency(v, dep) => write!(f, "Migration version {} depends on unapplied version {}", v, dep), - Self::InvalidSequence => write!(f, "Invalid migration sequence: versions must be consecutive"), - Self::UnsupportedVersion(v, min, max) => write!(f, "Database schema version {} is not supported (minimum: {}, maximum: {})", v, min, max), - Self::MigrationFailed(msg) => write!(f, "Migration failed: {}", msg), - } - } + #[error("Migration failed: {0}")] + MigrationFailed(String), } -impl std::error::Error for MigrationError {} - pub struct Database { pub(crate) conn: Connection, } @@ -118,7 +114,7 @@ impl Database { self.conn.execute_batch(SCHEMA)?; self.ensure_column("wallets", "secret_key", "TEXT")?; self.ensure_column("wallets", "rotation_history", "TEXT NOT NULL DEFAULT '[]'")?; - + // Run migrations if this is not a fresh database if self.get_meta("schema_version").is_ok() { self.run_migrations()?; @@ -127,7 +123,7 @@ impl Database { self.set_meta("schema_version", &CURRENT_SCHEMA_VERSION.to_string())?; self.record_migration(CURRENT_SCHEMA_VERSION, "initial_schema")?; } - + // The feature-flags schema is shipped alongside the rest of the // schema for first-startup convenience; subsequent startups hit the // idempotent `CREATE TABLE IF NOT EXISTS` guards and no-op. @@ -150,7 +146,7 @@ impl Database { /// Get all applied migrations from the database pub fn get_applied_migrations(&self) -> Result> { let mut stmt = self.conn.prepare( - "SELECT version, name, applied_at, checksum FROM schema_migrations ORDER BY version" + "SELECT version, name, applied_at, checksum FROM schema_migrations ORDER BY version", )?; let rows = stmt.query_map([], |row| { Ok(AppliedMigration { @@ -175,6 +171,9 @@ impl Database { } /// Remove a migration record from the database + // Not currently called from any code path in this crate. Kept rather than + // removed since deleting it is a product decision, not a lint-scoping one. + #[allow(dead_code)] fn remove_migration(&self, version: i64) -> Result<()> { self.conn.execute( "DELETE FROM schema_migrations WHERE version = ?1", @@ -188,17 +187,22 @@ impl Database { let mut hasher = Sha256::new(); hasher.update(version.to_string().as_bytes()); hasher.update(name.as_bytes()); - Ok(hasher.finalize().iter().map(|b| format!("{:02x}", b)).collect()) + Ok(hasher + .finalize() + .iter() + .map(|b| format!("{:02x}", b)) + .collect()) } /// Run pending migrations to bring the database to the current schema version pub fn run_migrations(&self) -> Result { let current_version = self.get_current_schema_version()?; let applied = self.get_applied_migrations()?; - let applied_versions: std::collections::HashSet = applied.iter().map(|m| m.version).collect(); - + let applied_versions: std::collections::HashSet = + applied.iter().map(|m| m.version).collect(); + let mut migrations_applied = Vec::new(); - + // Check if we need to upgrade if current_version < CURRENT_SCHEMA_VERSION { // Apply migrations from current_version + 1 to CURRENT_SCHEMA_VERSION @@ -209,7 +213,7 @@ impl Database { } } } - + Ok(MigrationResult { current_version: CURRENT_SCHEMA_VERSION, migrations_applied, @@ -219,11 +223,12 @@ impl Database { /// Apply a single migration within a transaction fn apply_migration(&self, version: i64) -> Result<()> { - let migration = self.get_migration(version) + let migration = self + .get_migration(version) .ok_or_else(|| anyhow::anyhow!("Migration version {} not found", version))?; - + let tx = self.conn.unchecked_transaction()?; - + // Apply the migration match migration.up(&tx) { Ok(()) => { @@ -234,13 +239,13 @@ impl Database { "INSERT INTO schema_migrations (version, name, applied_at, checksum) VALUES (?1, ?2, ?3, ?4)", params![version, migration.description(), applied_at, checksum], )?; - + // Update schema version tx.execute( "UPDATE meta SET value = ?1 WHERE key = 'schema_version'", params![version.to_string()], )?; - + tx.commit()?; Ok(()) } @@ -254,29 +259,37 @@ impl Database { /// Rollback a single migration within a transaction pub fn rollback_migration(&self, version: i64) -> Result<()> { let applied = self.get_applied_migrations()?; - let current_version = self.get_current_schema_version()?; - + let _current_version = self.get_current_schema_version()?; + // Check if the migration is applied if !applied.iter().any(|m| m.version == version) { - return Err(anyhow::anyhow!("Migration version {} is not applied", version)); + return Err(anyhow::anyhow!( + "Migration version {} is not applied", + version + )); } - + // Check if we can rollback (must be the latest applied migration) - let max_applied = applied.iter().map(|m| m.version).max() + let max_applied = applied + .iter() + .map(|m| m.version) + .max() .ok_or_else(|| anyhow::anyhow!("No migrations applied"))?; - + if version != max_applied { return Err(anyhow::anyhow!( "Can only rollback the latest migration ({}), tried to rollback {}", - max_applied, version + max_applied, + version )); } - - let migration = self.get_migration(version) + + let migration = self + .get_migration(version) .ok_or_else(|| anyhow::anyhow!("Migration version {} not found", version))?; - + let tx = self.conn.unchecked_transaction()?; - + // Rollback the migration match migration.down(&tx) { Ok(()) => { @@ -285,20 +298,24 @@ impl Database { "DELETE FROM schema_migrations WHERE version = ?1", params![version], )?; - + // Update schema version to previous version let previous_version = if version > 1 { version - 1 } else { 0 }; tx.execute( "UPDATE meta SET value = ?1 WHERE key = 'schema_version'", params![previous_version.to_string()], )?; - + tx.commit()?; Ok(()) } Err(e) => { let _ = tx.rollback(); - Err(anyhow::anyhow!("Rollback of migration {} failed: {}", version, e)) + Err(anyhow::anyhow!( + "Rollback of migration {} failed: {}", + version, + e + )) } } } @@ -873,7 +890,7 @@ impl Database { } ExportFormat::Csv => { let mut wtr = csv::Writer::from_writer(writer); - wtr.write_record(&[ + wtr.write_record([ "id", "event_type", "contract_id", @@ -884,7 +901,7 @@ impl Database { "network", ])?; for event in events { - wtr.write_record(&[ + wtr.write_record([ &event.id, &event.event_type, &event.contract_id, @@ -1108,12 +1125,12 @@ impl Migration for MigrationV1 { fn version(&self) -> i64 { 1 } - + fn description(&self) -> &str { "initial_schema" } - - fn up(&self, conn: &Connection) -> Result<()> { + + fn up(&self, _conn: &Connection) -> Result<()> { // This is a no-op since the initial schema is already applied in SCHEMA Ok(()) } @@ -1128,7 +1145,7 @@ impl Migration for MigrationV1 { DROP TABLE IF EXISTS networks; DROP TABLE IF EXISTS wallets; DROP TABLE IF EXISTS schema_migrations; - DROP TABLE IF EXISTS meta;" + DROP TABLE IF EXISTS meta;", )?; Ok(()) } @@ -1295,13 +1312,13 @@ mod tests { fn migration_rollback_latest_migration() { let db = in_memory_db(); let version_before = db.get_current_schema_version().unwrap(); - + // Rollback the latest migration db.rollback_migration(version_before).unwrap(); - + let version_after = db.get_current_schema_version().unwrap(); assert_eq!(version_after, version_before - 1); - + let applied = db.get_applied_migrations().unwrap(); assert!(!applied.iter().any(|m| m.version == version_before)); } @@ -1360,7 +1377,7 @@ mod tests { let db = in_memory_db(); let migration = MigrationV1 {}; let mut conn = db.conn; - + // Verify tables exist before rollback let table_count: i64 = conn .query_row( @@ -1370,10 +1387,10 @@ mod tests { ) .unwrap(); assert!(table_count > 0); - + // Rollback migration.down(&mut conn).unwrap(); - + // Verify tables are dropped let table_count_after: i64 = conn .query_row( @@ -1397,11 +1414,13 @@ mod tests { fn migration_transaction_rollback_on_failure() { let db = in_memory_db(); // Set schema version to 0 to simulate an old database - db.conn.execute( - "UPDATE meta SET value = '0' WHERE key = 'schema_version'", - [], - ).unwrap(); - + db.conn + .execute( + "UPDATE meta SET value = '0' WHERE key = 'schema_version'", + [], + ) + .unwrap(); + // This should apply migration 1 let result = db.run_migrations().unwrap(); assert_eq!(result.current_version, CURRENT_SCHEMA_VERSION); diff --git a/src/utils/debugger.rs b/src/utils/debugger.rs index a2727dce..72f4ff39 100644 --- a/src/utils/debugger.rs +++ b/src/utils/debugger.rs @@ -195,6 +195,9 @@ impl Debugger { None } + // Not currently called from any code path in this crate. Kept rather than + // removed since deleting it is a product decision, not a lint-scoping one. + #[allow(dead_code)] fn evaluate_condition(&self, _condition: &str) -> bool { true } diff --git a/src/utils/deployment_automation.rs b/src/utils/deployment_automation.rs index 298c4b13..174a542d 100644 --- a/src/utils/deployment_automation.rs +++ b/src/utils/deployment_automation.rs @@ -3,7 +3,7 @@ //! Provides AI-driven automation for deployment processes, including //! pre-deployment checks, automated testing, and deployment execution. -use anyhow::{Context, Result}; +use anyhow::Result; use serde::{Deserialize, Serialize}; use sha2::Digest; use std::path::Path; @@ -416,7 +416,7 @@ pub struct AutomatedTestRunner; impl AutomatedTestRunner { /// Run automated tests on the contract. - pub fn run_tests(wasm_path: &str) -> Result { + pub fn run_tests(_wasm_path: &str) -> Result { // Simulated test results let test_results = vec![ TestResult { @@ -475,7 +475,7 @@ impl DeploymentExecutor { status: "success".to_string(), contract_id: Some(format!( "C{}", - &hex::encode(&sha2::Sha256::digest(&wasm_bytes))[..56] + &hex::encode(sha2::Sha256::digest(&wasm_bytes))[..56] )), transaction_hash: Some(format!("tx_{}", uuid::Uuid::new_v4())), gas_used, diff --git a/src/utils/deployment_monitor.rs b/src/utils/deployment_monitor.rs index 5c300383..76000675 100644 --- a/src/utils/deployment_monitor.rs +++ b/src/utils/deployment_monitor.rs @@ -54,7 +54,7 @@ pub fn analyze_deployments( .into_iter() .filter(|record| { record.network == network - && contract_id.map_or(true, |cid| record.contract_id.as_deref() == Some(cid)) + && contract_id.is_none_or(|cid| record.contract_id.as_deref() == Some(cid)) }) .collect(); @@ -137,9 +137,7 @@ pub fn analyze_deployments( predictions.push(DeploymentPrediction { title: "Rollback risk is increasing".to_string(), confidence: 74, - detail: format!( - "The recent failure pattern suggests a higher chance of another deployment failure in the next rollout.", - ), + detail: "The recent failure pattern suggests a higher chance of another deployment failure in the next rollout.".to_string(), recommended_action: "Prepare a rollback plan, verify the artifact hash, and keep the previous deployment ready for immediate recovery.".to_string(), }); } @@ -148,9 +146,7 @@ pub fn analyze_deployments( predictions.push(DeploymentPrediction { title: "Performance degradation is likely".to_string(), confidence: 68, - detail: format!( - "The observed deployment latency trend points to slower execution than the recent baseline.", - ), + detail: "The observed deployment latency trend points to slower execution than the recent baseline.".to_string(), recommended_action: "Trim the deployment payload, validate the wallet setup, and review network congestion before launching the next deployment.".to_string(), }); } diff --git a/src/utils/deployment_monitoring_service.rs b/src/utils/deployment_monitoring_service.rs index e2b62ae0..dbd82b5d 100644 --- a/src/utils/deployment_monitoring_service.rs +++ b/src/utils/deployment_monitoring_service.rs @@ -1,4 +1,3 @@ -use anyhow::Result; use chrono::Utc; use colored::*; use serde::{Deserialize, Serialize}; diff --git a/src/utils/doc_generator.rs b/src/utils/doc_generator.rs index 2f6a879e..8a9f5875 100644 --- a/src/utils/doc_generator.rs +++ b/src/utils/doc_generator.rs @@ -694,21 +694,21 @@ impl HtmlDocGenerator { .functions .iter() .filter(|f| f.visibility == Visibility::Public) - .map(|f| render_function_card(f)) + .map(render_function_card) .collect::>() .join("\n"); let structs_html = docs .structs .iter() - .map(|s| render_struct_card(s)) + .map(render_struct_card) .collect::>() .join("\n"); let enums_html = docs .enums .iter() - .map(|e| render_enum_card(e)) + .map(render_enum_card) .collect::>() .join("\n"); @@ -1114,7 +1114,7 @@ fn render_function_card(f: &ExtractedFn) -> String { .doc_comment .lines() .filter(|l| !l.starts_with("```")) - .map(|l| escape_html(l)) + .map(escape_html) .collect::>() .join("
"); diff --git a/src/utils/doc_templates.rs b/src/utils/doc_templates.rs index 822781c3..dbcae3af 100644 --- a/src/utils/doc_templates.rs +++ b/src/utils/doc_templates.rs @@ -9,7 +9,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; // ────────────────────────────────────────────────────────────────────────────── // Template context diff --git a/src/utils/docs.rs b/src/utils/docs.rs index dee6944b..92599f18 100644 --- a/src/utils/docs.rs +++ b/src/utils/docs.rs @@ -1,6 +1,5 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; @@ -115,6 +114,10 @@ fn contract_doc_dir(contract_id: &str) -> Result { Ok(dir) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] pub fn generate_documentation( contract_id: &str, name: &str, diff --git a/src/utils/documentation.rs b/src/utils/documentation.rs index 6bfe0a58..2e1897fa 100644 --- a/src/utils/documentation.rs +++ b/src/utils/documentation.rs @@ -1,6 +1,5 @@ -use anyhow::{Context, Result}; +use anyhow::Result; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; @@ -161,7 +160,10 @@ impl DocumentationGenerator { Ok(documentation) } - fn extract_functions_from_wasm(&self, wasm_bytes: &[u8]) -> Result> { + // Multi-field struct literals read more clearly as sequential pushes + // than as one large `vec![]` literal. + #[allow(clippy::vec_init_then_push)] + fn extract_functions_from_wasm(&self, _wasm_bytes: &[u8]) -> Result> { // Simplified function extraction - in production would use proper WASM parsing let mut functions = Vec::new(); @@ -228,7 +230,7 @@ impl DocumentationGenerator { }; // Check if contract already exists in index - if let Some(existing) = index + if let Some(_existing) = index .contracts .iter() .find(|c| c.contract_id == documentation.contract_id) @@ -589,7 +591,7 @@ impl DocumentationVersionManager { let entry = entry?; let path = entry.path(); - if path.extension().map_or(false, |ext| ext == "json") { + if path.extension().is_some_and(|ext| ext == "json") { let content = fs::read_to_string(&path)?; let version: DocumentationVersion = serde_json::from_str(&content)?; versions.push(version); diff --git a/src/utils/feature_flags.rs b/src/utils/feature_flags.rs index 8e7dae17..e7b320f0 100644 --- a/src/utils/feature_flags.rs +++ b/src/utils/feature_flags.rs @@ -25,7 +25,6 @@ use crate::utils::database::Database; use anyhow::{bail, Context, Result}; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; use std::sync::{Mutex, OnceLock}; @@ -119,7 +118,7 @@ impl SegmentRule { bucket < (*percent as u32).min(100) } SegmentRule::HasAttribute { key, any_of } => match ctx.attributes.get(key) { - Some(v) if any_of.is_empty() => true, + Some(_v) if any_of.is_empty() => true, Some(v) => any_of.iter().any(|cand| cand == v), None => false, }, @@ -631,7 +630,7 @@ impl Database { "SELECT flag_name, version, enabled, rollout_percent, segments_json, variants_json, note, created_at \ FROM flag_states WHERE flag_name = ?1 ORDER BY version ASC", )?; - let rows = stmt.query_map(rusqlite::params![flag_name], |row| row_to_state(row))?; + let rows = stmt.query_map(rusqlite::params![flag_name], row_to_state)?; rows.map(|r| r.map_err(anyhow::Error::from)).collect() } @@ -658,7 +657,7 @@ impl Database { ) latest ON latest.flag_name = s.flag_name AND latest.v = s.version \ ORDER BY s.flag_name", )?; - let rows = stmt.query_map([], |row| row_to_state(row))?; + let rows = stmt.query_map([], row_to_state)?; rows.map(|r| r.map_err(anyhow::Error::from)).collect() } diff --git a/src/utils/gas_analyzer.rs b/src/utils/gas_analyzer.rs index 4bcefc05..eeca72e3 100644 --- a/src/utils/gas_analyzer.rs +++ b/src/utils/gas_analyzer.rs @@ -601,7 +601,7 @@ pub fn generate_findings(bytes: &[u8], profile: &WasmSectionProfile) -> Vec 0 { + let instr_per_byte = if !bytes.is_empty() { profile.estimated_instruction_count as f64 / bytes.len() as f64 } else { 0.0 diff --git a/src/utils/hardware_wallet.rs b/src/utils/hardware_wallet.rs index e0ae90e8..a86ba5e9 100644 --- a/src/utils/hardware_wallet.rs +++ b/src/utils/hardware_wallet.rs @@ -1,3 +1,11 @@ +// The Ledger APDU constants and codec helpers below (build_apdu, +// frame_apdu_for_hid, parse_hd_path, encode_hd_path, extract_*_bytes) are +// exercised by the `hardware-wallet` feature's transport implementation and +// by this module's own unit tests, but not by a default build with the +// feature off and no tests compiled — that's the one configuration where +// they're genuinely unused, not a sign anything here is actually dead. +#![cfg_attr(not(any(test, feature = "hardware-wallet")), allow(dead_code))] + use anyhow::{Context, Result}; use clap::ValueEnum; diff --git a/src/utils/migration_ai.rs b/src/utils/migration_ai.rs index e889a483..49fe49f5 100644 --- a/src/utils/migration_ai.rs +++ b/src/utils/migration_ai.rs @@ -242,7 +242,7 @@ fn analyze_type_changes( old_specs: &[String], new_specs: &[String], breaking_changes: &mut Vec, - _suggestions: &mut Vec, + _suggestions: &mut [MigrationSuggestion], ) { let old_types = extract_types(old_specs); let new_types = extract_types(new_specs); @@ -281,7 +281,7 @@ fn analyze_storage_layout( new_specs: &[String], storage_changes: &mut Vec, breaking_changes: &mut Vec, - _suggestions: &mut Vec, + _suggestions: &mut [MigrationSuggestion], ) { let old_storage = extract_storage_keys(old_specs); let new_storage = extract_storage_keys(new_specs); @@ -403,7 +403,7 @@ fn analyze_sdk_upgrade( fn analyze_protocol_upgrade( config: &AnalysisConfig, breaking_changes: &mut Vec, - _suggestions: &mut Vec, + _suggestions: &mut [MigrationSuggestion], ) { match (config.old_protocol_version, config.new_protocol_version) { (Some(old), Some(new)) if old != new => { @@ -446,7 +446,7 @@ fn determine_compatibility(changes: &[BreakingChange]) -> Compatibility { fn build_migration_steps( config: &AnalysisConfig, - breaking_changes: &[BreakingChange], + _breaking_changes: &[BreakingChange], storage_changes: &[StorageChange], old_wasm_hash: &str, new_wasm_hash: &str, @@ -456,7 +456,7 @@ fn build_migration_steps( let mut order = 1usize; steps.push(MigrationStep { - order: order, + order, action: "backup".into(), description: "Create a backup of the current contract state and WASM".into(), command: Some("starforge backup create --contract ".into()), @@ -467,7 +467,7 @@ fn build_migration_steps( if has_storage_migration { steps.push(MigrationStep { - order: order, + order, action: "export_storage".into(), description: "Export current contract storage to a snapshot".into(), command: Some( @@ -514,7 +514,7 @@ fn build_migration_steps( ); steps.push(MigrationStep { - order: order, + order, action: "create_rules".into(), description: "Create migration rules file for storage transformation".into(), command: Some("starforge migrate init --from-version --to-version ".into()), @@ -524,7 +524,7 @@ fn build_migration_steps( order += 1; steps.push(MigrationStep { - order: order, + order, action: "test_migration".into(), description: "Dry-run migration to verify rules produce expected output".into(), command: Some( @@ -536,7 +536,7 @@ fn build_migration_steps( order += 1; steps.push(MigrationStep { - order: order, + order, action: "apply_migration".into(), description: "Apply storage migration to produce transformed snapshot".into(), command: Some("starforge migrate run --contract-id --snapshot snapshot.json --rules rules.json --output migrated-snapshot.json".into()), @@ -547,7 +547,7 @@ fn build_migration_steps( } steps.push(MigrationStep { - order: order, + order, action: "generate_migration_code".into(), description: "Generate on-chain migration function in the new contract".into(), command: Some("starforge migrate-ai generate --old-wasm --new-wasm --output migration.rs".into()), @@ -563,7 +563,7 @@ fn build_migration_steps( } steps.push(MigrationStep { - order: order, + order, action: "compatibility_check".into(), description: "Run final compatibility check between old and new WASM".into(), command: Some("starforge upgrade-auto compat --old-wasm --new-wasm ".into()), @@ -573,7 +573,7 @@ fn build_migration_steps( order += 1; steps.push(MigrationStep { - order: order, + order, action: "upgrade_contract".into(), description: "Upgrade the contract to the new WASM version".into(), command: Some("starforge upgrade execute --contract-id --wasm --wallet ".into()), @@ -595,9 +595,7 @@ fn generate_migration_code_stub( snippet.push_str(&format!("// Migration function for {}\n", contract_name)); snippet.push_str("// Generated by starforge migrate-ai\n\n"); snippet.push_str("#[allow(unused)]\n"); - snippet.push_str(&format!( - "pub fn migrate(env: &soroban_sdk::Env, admin: soroban_sdk::Address) {{\n" - )); + snippet.push_str("pub fn migrate(env: &soroban_sdk::Env, admin: soroban_sdk::Address) {\n"); snippet.push_str(" admin.require_auth();\n\n"); for sc in storage_changes { @@ -763,9 +761,7 @@ pub fn extract_spec_entries(wasm_bytes: &[u8]) -> Result> { let content = String::from_utf8_lossy(meta_section); for line in content.lines() { let trimmed = line.trim(); - if trimmed.starts_with("SDK_VERSION:") { - specs.push(format!("meta:{}", trimmed)); - } else if trimmed.starts_with("PROTOCOL_VERSION:") { + if trimmed.starts_with("SDK_VERSION:") || trimmed.starts_with("PROTOCOL_VERSION:") { specs.push(format!("meta:{}", trimmed)); } } diff --git a/src/utils/network_simulator/deterministic.rs b/src/utils/network_simulator/deterministic.rs index 925166e6..368ea7d3 100644 --- a/src/utils/network_simulator/deterministic.rs +++ b/src/utils/network_simulator/deterministic.rs @@ -7,7 +7,6 @@ use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; -use std::fmt; use std::sync::Mutex; // ── Deterministic Configuration ─────────────────────────────────────────────── diff --git a/src/utils/network_simulator/failure.rs b/src/utils/network_simulator/failure.rs index 8d857189..6b483ab8 100644 --- a/src/utils/network_simulator/failure.rs +++ b/src/utils/network_simulator/failure.rs @@ -4,7 +4,6 @@ //! simulator to test how contracts and clients handle errors. use serde::{Deserialize, Serialize}; -use std::collections::HashMap; // ── Failure Modes ───────────────────────────────────────────────────────────── diff --git a/src/utils/network_simulator/scenarios.rs b/src/utils/network_simulator/scenarios.rs index e0b6b46b..2216857e 100644 --- a/src/utils/network_simulator/scenarios.rs +++ b/src/utils/network_simulator/scenarios.rs @@ -3,7 +3,7 @@ //! Pre-built, parameterizable test scenarios that set up the simulator //! with realistic contract + account states for reproducible testing. -use crate::utils::network_simulator::deterministic::{derive_contract_id, derive_public_key}; +use crate::utils::network_simulator::deterministic::derive_public_key; use crate::utils::network_simulator::simulator::{NetworkSimulator, SimulatorConfig}; use anyhow::Result; use serde::{Deserialize, Serialize}; @@ -314,7 +314,7 @@ impl ScenarioRunner { } } - fn load_test(seed: u64) -> Scenario { + fn load_test(_seed: u64) -> Scenario { let mut accounts = Vec::new(); for i in 0..10 { accounts.push(ScenarioAccount { diff --git a/src/utils/network_simulator/simulator.rs b/src/utils/network_simulator/simulator.rs index e722a72b..43000d64 100644 --- a/src/utils/network_simulator/simulator.rs +++ b/src/utils/network_simulator/simulator.rs @@ -6,12 +6,9 @@ use crate::utils::network_simulator::deterministic::{ derive_contract_id, derive_public_key, derive_tx_hash, DeterministicConfig, SeededRng, }; -use crate::utils::network_simulator::failure::{ - failure_to_rpc_error, FailureInjector, FailureMode, -}; +use crate::utils::network_simulator::failure::{failure_to_rpc_error, FailureInjector}; use crate::utils::network_simulator::state::SnapshotManager; -use crate::utils::network_simulator::time::{LedgerTime, TimeController}; -use chrono::Utc; +use crate::utils::network_simulator::time::TimeController; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::collections::HashMap; diff --git a/src/utils/network_simulator/state.rs b/src/utils/network_simulator/state.rs index 0cab93f9..30a25b7e 100644 --- a/src/utils/network_simulator/state.rs +++ b/src/utils/network_simulator/state.rs @@ -67,6 +67,10 @@ impl SnapshotManager { } /// Take a snapshot of the current simulator state. + // Each parameter is an independent, named input (CLI flags / distinct config + // values); bundling them into a struct here would add indirection without + // reducing real complexity. + #[allow(clippy::too_many_arguments)] pub fn take_snapshot( &mut self, label: &str, @@ -127,7 +131,7 @@ impl SnapshotManager { if path.exists() { if let Ok(json) = fs::read_to_string(&path) { if let Ok(snapshot) = serde_json::from_str::(&json) { - let label = snapshot.label.clone(); + let _label = snapshot.label.clone(); self.snapshots.insert(id.to_string(), snapshot); return self.snapshots.get(id); } diff --git a/src/utils/network_simulator/time.rs b/src/utils/network_simulator/time.rs index cefc22ac..ec60af06 100644 --- a/src/utils/network_simulator/time.rs +++ b/src/utils/network_simulator/time.rs @@ -3,7 +3,7 @@ //! Provides ledger time manipulation for testing – advance, freeze, rewind, //! and jump to specific timestamps. -use chrono::{DateTime, Duration, Utc}; +use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; /// Represents the simulated ledger time. diff --git a/src/utils/notifications.rs b/src/utils/notifications.rs index a1507924..cb836afe 100644 --- a/src/utils/notifications.rs +++ b/src/utils/notifications.rs @@ -151,9 +151,9 @@ pub fn send_notification( Ok(()) } -fn send_email(destination: &str, _template: &str, data: &HashMap) -> Result<()> { +fn send_email(destination: &str, _template: &str, _data: &HashMap) -> Result<()> { info(&format!("Email notification queued to {}", destination)); - return Ok(()); + Ok(()) } fn send_slack(destination: &str, _template: &str, data: &HashMap) -> Result<()> { diff --git a/src/utils/orchestration.rs b/src/utils/orchestration.rs index 7e4d7115..83243342 100644 --- a/src/utils/orchestration.rs +++ b/src/utils/orchestration.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::thread; @@ -200,7 +200,7 @@ impl OrchestrationEngine { let mut temp_visited = HashSet::new(); for contract_id in &contract_ids { - self.topological_sort( + Self::topological_sort( contract_id, &plan.dependencies, &mut visited, @@ -213,7 +213,6 @@ impl OrchestrationEngine { } fn topological_sort( - &self, contract_id: &str, dependencies: &HashMap>, visited: &mut HashSet, @@ -232,7 +231,7 @@ impl OrchestrationEngine { if let Some(deps) = dependencies.get(contract_id) { for dep in deps { - self.topological_sort(dep, dependencies, visited, temp_visited, sorted)?; + Self::topological_sort(dep, dependencies, visited, temp_visited, sorted)?; } } @@ -478,7 +477,7 @@ impl OrchestrationEngine { let entry = entry?; let path = entry.path(); - if path.extension().map_or(false, |ext| ext == "json") { + if path.extension().is_some_and(|ext| ext == "json") { let content = fs::read_to_string(&path)?; let plan: DeploymentPlan = serde_json::from_str(&content)?; plans.push(plan); @@ -502,7 +501,7 @@ impl OrchestrationEngine { Ok(plan) } - fn load_plan_by_execution(&self, execution_id: &str) -> Result { + fn load_plan_by_execution(&self, _execution_id: &str) -> Result { // In production, would store execution-to-plan mapping // For now, load the first plan let plans = self.list_plans()?; diff --git a/src/utils/pattern_library.rs b/src/utils/pattern_library.rs index 557ab806..764dcb1d 100644 --- a/src/utils/pattern_library.rs +++ b/src/utils/pattern_library.rs @@ -10,11 +10,10 @@ //! indicators before the LLM call, giving the model a structured head-start. use anyhow::{Context, Result}; -use chrono::Utc; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use crate::utils::config; diff --git a/src/utils/performance.rs b/src/utils/performance.rs index 6daa305a..0f692850 100644 --- a/src/utils/performance.rs +++ b/src/utils/performance.rs @@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; use std::collections::HashMap; use std::fs; -use std::path::{Path, PathBuf}; -use std::time::{Duration, Instant}; +use std::path::PathBuf; +use std::time::Instant; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ContractMetrics { @@ -369,7 +369,7 @@ pub fn analyze_bottlenecks(contract_id: &str) -> Result { .or_insert(record.gas_used); } - let total_gas: u64 = gas_history.iter().map(|r| r.gas_used).sum(); + let _total_gas: u64 = gas_history.iter().map(|r| r.gas_used).sum(); let total_executions = gas_history.len() as f64; let bottleneck_operations: Vec = operation_frequencies diff --git a/src/utils/profiler.rs b/src/utils/profiler.rs index 0373f894..b7ef48fd 100644 --- a/src/utils/profiler.rs +++ b/src/utils/profiler.rs @@ -1,4 +1,3 @@ -use std::mem::size_of; use std::time::{Duration, Instant}; #[cfg(feature = "memory-profiling")] @@ -125,6 +124,9 @@ pub struct Profiler { #[cfg(feature = "memory-profiling")] #[derive(Debug)] struct MemoryTracker { + // Not currently called from any code path in this crate. Kept rather than + // removed since deleting it is a product decision, not a lint-scoping one. + #[allow(dead_code)] start: Instant, current_memory: usize, peak_memory: usize, @@ -143,10 +145,6 @@ impl MemoryTracker { } } -#[cfg(not(feature = "memory-profiling"))] -#[derive(Debug)] -struct MemoryTracker; - impl Profiler { pub fn start() -> Self { #[cfg(feature = "memory-profiling")] @@ -156,8 +154,6 @@ impl Profiler { peak_memory: 0, samples: Vec::new(), }); - #[cfg(not(feature = "memory-profiling"))] - let memory_tracker: Option = None; Self { start: Instant::now(), diff --git a/src/utils/prompt_manager.rs b/src/utils/prompt_manager.rs index 42acbdbc..6da6bf88 100644 --- a/src/utils/prompt_manager.rs +++ b/src/utils/prompt_manager.rs @@ -4,6 +4,9 @@ use rusqlite::{params, Connection}; use serde_json::Value; use std::path::PathBuf; +/// (prompt_name, version_tag, uses, successes, failures, avg_rating) +pub type PromptStats = (String, String, i64, i64, i64, f64); + pub struct PromptManager { conn: Connection, } @@ -257,7 +260,7 @@ impl PromptManager { Ok(prompts) } - pub fn get_stats(&self) -> Result> { + pub fn get_stats(&self) -> Result> { let mut stmt = self.conn.prepare( "SELECT p.name, v.version_tag, a.uses, a.successes, a.failures, CAST(a.rating_sum AS REAL) / NULLIF(a.rating_count, 0) diff --git a/src/utils/quality_analysis.rs b/src/utils/quality_analysis.rs index 6d0b466c..33ebf2de 100644 --- a/src/utils/quality_analysis.rs +++ b/src/utils/quality_analysis.rs @@ -445,14 +445,15 @@ fn score_best_practices(source: &str, metrics: &CodeMetrics) -> QualityCategory ); } - if source.contains("Map<") || source.contains("Vec<") { - if !source.contains("enum DataKey") && !source.contains("enum StorageKey") { - score -= 2; - findings.push( + if (source.contains("Map<") || source.contains("Vec<")) + && !source.contains("enum DataKey") + && !source.contains("enum StorageKey") + { + score -= 2; + findings.push( "No dedicated storage-key enum (DataKey/StorageKey) — consider one for type-safe storage access" .to_string(), ); - } } QualityCategory { diff --git a/src/utils/registry.rs b/src/utils/registry.rs index 79d2ab2e..28ce4e6c 100644 --- a/src/utils/registry.rs +++ b/src/utils/registry.rs @@ -2,7 +2,6 @@ use crate::utils::http_client; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::fs; -use std::path::PathBuf; /// Configuration for the remote template registry. #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src/utils/repl.rs b/src/utils/repl.rs index cfb60a57..5ce56bdf 100644 --- a/src/utils/repl.rs +++ b/src/utils/repl.rs @@ -4,7 +4,6 @@ use rustyline::completion::{Completer, Pair}; use rustyline::error::ReadlineError; use rustyline::highlight::Highlighter; use rustyline::hint::Hinter; -use rustyline::history::History; use rustyline::validate::Validator; use rustyline::{Context, Editor, Helper}; use std::collections::HashSet; diff --git a/src/utils/scheduler.rs b/src/utils/scheduler.rs index 9d4893c7..f7a66875 100644 --- a/src/utils/scheduler.rs +++ b/src/utils/scheduler.rs @@ -79,6 +79,10 @@ pub fn parse_when(when: &str) -> Result> { ) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] pub fn create( contract_id: String, wasm: PathBuf, diff --git a/src/utils/security/ai_audit.rs b/src/utils/security/ai_audit.rs index 8fba84ba..3f503c2c 100644 --- a/src/utils/security/ai_audit.rs +++ b/src/utils/security/ai_audit.rs @@ -3,9 +3,7 @@ //! Combines static pattern analysis with Claude AI for comprehensive //! vulnerability detection with < 15% false positive rate. -use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; /// Security vulnerability with full context. #[derive(Debug, Clone, Serialize, Deserialize)] @@ -158,8 +156,8 @@ impl SecurityPatterns { // A state write *after* the external call is the CEI violation. // A transfer that happens last is the safe ordering. let mut found_storage_after = false; - for j in (i + 1)..std::cmp::min(i + 10, lines.len()) { - let candidate = lines[j].trim(); + for candidate in &lines[(i + 1)..std::cmp::min(i + 10, lines.len())] { + let candidate = candidate.trim(); if candidate.contains("storage") && candidate.contains("set") { found_storage_after = true; break; @@ -199,10 +197,10 @@ impl SecurityPatterns { { // Look for require_auth in next 20 lines let mut has_auth = false; - for j in (i + 1)..std::cmp::min(i + 20, lines.len()) { + for line_j in &lines[(i + 1)..std::cmp::min(i + 20, lines.len())] { // Ignore comments: a line reading `// Missing // require_auth() check` must not satisfy the check. - let code_only = lines[j].split("//").next().unwrap_or(lines[j]); + let code_only = line_j.split("//").next().unwrap_or(line_j); if code_only.contains("require_auth") { has_auth = true; break; @@ -322,8 +320,10 @@ impl SecurityPatterns { if line.contains("persistent()") && line.contains("set") { // Look for extend_ttl in nearby lines let mut has_ttl = false; - for j in std::cmp::max(0, i.saturating_sub(5))..std::cmp::min(i + 5, lines.len()) { - if lines[j].contains("extend_ttl") { + for nearby in + &lines[std::cmp::max(0, i.saturating_sub(5))..std::cmp::min(i + 5, lines.len())] + { + if nearby.contains("extend_ttl") { has_ttl = true; break; } diff --git a/src/utils/security/ai_audit_service.rs b/src/utils/security/ai_audit_service.rs index 3d18f62c..5ec19209 100644 --- a/src/utils/security/ai_audit_service.rs +++ b/src/utils/security/ai_audit_service.rs @@ -2,12 +2,11 @@ use super::ai_audit::{ build_fallback_report, build_system_prompt, build_user_prompt, run_static_checks, - AiAuditResponse, AuditLevel, AuditRequest, SecurityAuditReport, + AiAuditResponse, AuditRequest, SecurityAuditReport, }; use anyhow::{anyhow, Result}; use chrono::Utc; use reqwest::Client; -use serde_json::json; /// Anthropic API message format. #[derive(serde::Serialize)] diff --git a/src/utils/security/audit.rs b/src/utils/security/audit.rs index 31e3c895..c5c61248 100644 --- a/src/utils/security/audit.rs +++ b/src/utils/security/audit.rs @@ -112,6 +112,10 @@ pub fn run_audit(path: &Path, config: &AuditConfig) -> Result { }) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] fn collect_external_tool( tool: &str, env_vars: &[&str], diff --git a/src/utils/security/compliance.rs b/src/utils/security/compliance.rs index dd407fb3..7501f7d8 100644 --- a/src/utils/security/compliance.rs +++ b/src/utils/security/compliance.rs @@ -85,6 +85,12 @@ pub struct ComplianceEngine { rules: Vec, } +impl Default for ComplianceEngine { + fn default() -> Self { + Self::new() + } +} + impl ComplianceEngine { pub fn new() -> Self { let rules = vec![ @@ -259,7 +265,7 @@ impl ComplianceEngine { .map(|r| r.remediation.clone()) .collect(); - let overall_risk = if critical_gaps.len() > 0 { + let overall_risk = if !critical_gaps.is_empty() { "critical" } else if failed_count > total / 2 { "high" diff --git a/src/utils/security/data_protection.rs b/src/utils/security/data_protection.rs index 9b45993b..9241b853 100644 --- a/src/utils/security/data_protection.rs +++ b/src/utils/security/data_protection.rs @@ -127,6 +127,10 @@ pub struct DataProtectionSummary { pub integrity_score: f64, } +// Fields not currently read from any code path in this crate. Kept rather +// than removed since deleting them is a product decision, not a +// lint-scoping one. +#[allow(dead_code)] pub struct DataProtectionEngine { encryption_policy: EncryptionPolicy, access_policy: AccessPolicy, @@ -134,6 +138,12 @@ pub struct DataProtectionEngine { classifications: HashMap, } +impl Default for DataProtectionEngine { + fn default() -> Self { + Self::new() + } +} + impl DataProtectionEngine { pub fn new() -> Self { let mut classifications = HashMap::new(); @@ -347,7 +357,7 @@ impl DataProtectionEngine { } fn check_key_management(&self, source: &str) -> DataProtectionCheck { - let has_key_ops = + let _has_key_ops = source.contains("key") || source.contains("secret") || source.contains("private"); let has_hardcoded = source.contains("\"sk1\"") || source.contains("\"secret_key\"") diff --git a/src/utils/security_scanner.rs b/src/utils/security_scanner.rs index cf7f8624..f21095b8 100644 --- a/src/utils/security_scanner.rs +++ b/src/utils/security_scanner.rs @@ -10,7 +10,7 @@ use crate::utils::security::audit::VulnerabilityFinding; use anyhow::{Context, Result}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::Command; // --------------------------------------------------------------------------- diff --git a/src/utils/social.rs b/src/utils/social.rs index b61eb691..ea138a91 100644 --- a/src/utils/social.rs +++ b/src/utils/social.rs @@ -1,8 +1,7 @@ use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Team { diff --git a/src/utils/soroban.rs b/src/utils/soroban.rs index b1a03e59..8994ac78 100644 --- a/src/utils/soroban.rs +++ b/src/utils/soroban.rs @@ -499,7 +499,7 @@ fn build_and_sign_transaction( function: &str, args: &[String], wallet: &WalletEntry, - network: &str, + _network: &str, signing: Option<&SigningRequest>, ) -> Result { let tx_xdr = build_transaction_xdr(contract_id, function, args)?; diff --git a/src/utils/state_diff.rs b/src/utils/state_diff.rs index 6514b143..d28430d7 100644 --- a/src/utils/state_diff.rs +++ b/src/utils/state_diff.rs @@ -1,4 +1,3 @@ -use anyhow::Result; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::BTreeMap; diff --git a/src/utils/stream.rs b/src/utils/stream.rs index 82486b2d..d71ea14d 100644 --- a/src/utils/stream.rs +++ b/src/utils/stream.rs @@ -274,9 +274,7 @@ impl SorobanEventStream { .websocket .as_mut() .ok_or_else(|| anyhow::anyhow!("WebSocket connection is not available"))?; - websocket - .send(Message::Text(request.to_string().into())) - .await + websocket.send(Message::Text(request.to_string())).await }; if let Err(err) = send_result { diff --git a/src/utils/template.rs b/src/utils/template.rs index a9ccd939..3dcc8d9c 100644 --- a/src/utils/template.rs +++ b/src/utils/template.rs @@ -1,4 +1,4 @@ -use crate::utils::{print as p, registry, template_analytics, templates}; +use crate::utils::{print as p, template_analytics, templates}; use anyhow::Result; use clap::Subcommand; use std::path::PathBuf; @@ -277,6 +277,10 @@ pub async fn handle(cmd: TemplateCommands) -> Result<()> { } } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn import( path: PathBuf, name: Option, @@ -307,6 +311,10 @@ async fn import( Ok(()) } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] async fn publish( path: PathBuf, name: Option, diff --git a/src/utils/template_analytics.rs b/src/utils/template_analytics.rs index c8b3db58..2383e4d1 100644 --- a/src/utils/template_analytics.rs +++ b/src/utils/template_analytics.rs @@ -601,7 +601,7 @@ fn build_issue_detection(entries: &[TemplateEntry], feedback: &[FeedbackEntry]) } if let Some(sr) = &e.security_review { if let Some(findings) = &sr.findings { - if findings.len() > 0 { + if !findings.is_empty() { reasons.push(format!("{} unresolved security finding(s)", findings)); } } diff --git a/src/utils/template_customization_ai.rs b/src/utils/template_customization_ai.rs index 1965143e..9ec8e7e8 100644 --- a/src/utils/template_customization_ai.rs +++ b/src/utils/template_customization_ai.rs @@ -2,7 +2,7 @@ use crate::utils::{ollama, template_vcs}; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CustomizationHistory { @@ -161,8 +161,8 @@ fn apply_ai_modifications(template_path: &Path, ai_response: &str) -> Result Vec { let mut sources = Vec::new(); @@ -119,9 +119,9 @@ pub fn analyze_template_directory( + external_call_score as u32 + batch_operations_score as u32) / 5) as u8; - let estimated_gas_reduction_percent = (100 - overall_score).max(5).min(40) as u8; - let estimated_speedup_percent = ((100 - overall_score) / 2).max(3).min(25) as u8; - let estimated_memory_savings_percent = ((100 - overall_score) / 3).max(2).min(15) as u8; + let estimated_gas_reduction_percent = (100 - overall_score).clamp(5, 40); + let estimated_speedup_percent = ((100 - overall_score) / 2).clamp(3, 25); + let estimated_memory_savings_percent = ((100 - overall_score) / 3).clamp(2, 15); Ok(TemplatePerformanceAnalysis { template_name: name, diff --git a/src/utils/template_recommender.rs b/src/utils/template_recommender.rs index 551dd13e..42873135 100644 --- a/src/utils/template_recommender.rs +++ b/src/utils/template_recommender.rs @@ -34,8 +34,11 @@ pub enum SkillLevel { } impl SkillLevel { - /// Parse from a case-insensitive string. - pub fn from_str(s: &str) -> Option { + /// Parse from a case-insensitive string, accepting common shorthand aliases. + /// + /// Not `FromStr::from_str` — this returns `Option`, not `Result`, since + /// there's no meaningful error type for "unrecognized skill level". + pub fn parse_lenient(s: &str) -> Option { match s.to_lowercase().as_str() { "beginner" | "b" | "novice" => Some(Self::Beginner), "intermediate" | "i" | "mid" | "medium" => Some(Self::Intermediate), @@ -307,9 +310,11 @@ fn skill_fit(entry: &templates::TemplateEntry, skill_level: SkillLevel) -> (&'st SkillLevel::Advanced => { if has_advanced { ("Excellent for advanced use", 15.0) - } else if entry.security_review.as_ref().map_or(false, |sr| { - sr.status == "audited" && sr.score.unwrap_or(0.0) >= 90.0 - }) { + } else if entry + .security_review + .as_ref() + .is_some_and(|sr| sr.status == "audited" && sr.score.unwrap_or(0.0) >= 90.0) + { ("Production-grade quality", 10.0) } else { ("Suitable", 0.0) @@ -656,12 +661,18 @@ mod tests { #[test] fn test_skill_level_from_str() { - assert_eq!(SkillLevel::from_str("beginner"), Some(SkillLevel::Beginner)); assert_eq!( - SkillLevel::from_str("INTERMEDIATE"), + SkillLevel::parse_lenient("beginner"), + Some(SkillLevel::Beginner) + ); + assert_eq!( + SkillLevel::parse_lenient("INTERMEDIATE"), Some(SkillLevel::Intermediate) ); - assert_eq!(SkillLevel::from_str("expert"), Some(SkillLevel::Advanced)); - assert_eq!(SkillLevel::from_str("unknown"), None); + assert_eq!( + SkillLevel::parse_lenient("expert"), + Some(SkillLevel::Advanced) + ); + assert_eq!(SkillLevel::parse_lenient("unknown"), None); } } diff --git a/src/utils/template_security_scanner.rs b/src/utils/template_security_scanner.rs index 5eb3d8c9..30dab9bd 100644 --- a/src/utils/template_security_scanner.rs +++ b/src/utils/template_security_scanner.rs @@ -120,8 +120,8 @@ impl KnownVulnerabilities { if line.contains("transfer") && !line.trim().starts_with("//") { // Check if state update happens after transfer let mut state_after = false; - for j in (i + 1)..std::cmp::min(i + 10, lines.len()) { - if lines[j].contains("storage") && lines[j].contains("set") { + for nearby in &lines[(i + 1)..std::cmp::min(i + 10, lines.len())] { + if nearby.contains("storage") && nearby.contains("set") { state_after = true; break; } @@ -150,12 +150,12 @@ impl KnownVulnerabilities { && (line.contains("&mut") || line.contains("env:")) { let mut has_auth = false; - for j in i..std::cmp::min(i + 20, lines.len()) { - if lines[j].contains("require_auth") { + for nearby in &lines[i..std::cmp::min(i + 20, lines.len())] { + if nearby.contains("require_auth") { has_auth = true; break; } - if lines[j].contains("pub fn ") { + if nearby.contains("pub fn ") { break; } } @@ -268,12 +268,12 @@ impl KnownVulnerabilities { for (i, line) in lines.iter().enumerate() { if line.contains("pub fn ") && (line.contains("admin") || line.contains("owner")) { let mut has_check = false; - for j in i..std::cmp::min(i + 20, lines.len()) { - if lines[j].contains("require_auth") || lines[j].contains("assert") { + for nearby in &lines[i..std::cmp::min(i + 20, lines.len())] { + if nearby.contains("require_auth") || nearby.contains("assert") { has_check = true; break; } - if lines[j].contains("pub fn ") { + if nearby.contains("pub fn ") { break; } } diff --git a/src/utils/template_version_ai.rs b/src/utils/template_version_ai.rs index f2352a32..8625e4a0 100644 --- a/src/utils/template_version_ai.rs +++ b/src/utils/template_version_ai.rs @@ -2,7 +2,6 @@ use crate::utils::ollama; use crate::utils::template_vcs::{get_version_history, TemplateChangelog, TemplateVersion}; use anyhow::{Context, Result}; use semver::Version; -use std::fs; use std::path::Path; use std::process::Command; @@ -334,9 +333,9 @@ fn extract_list(text: &str, section: &str) -> Vec { }; let slice = &text[start + section.len()..]; - let mut lines = slice.lines().skip_while(|l| l.trim().is_empty()); + let lines = slice.lines().skip_while(|l| l.trim().is_empty()); - while let Some(line) = lines.next() { + for line in lines { let line = line.trim(); if line.is_empty() || line.contains(':') && !line.starts_with('-') { break; diff --git a/src/utils/templates.rs b/src/utils/templates.rs index af10b811..6e40c84d 100644 --- a/src/utils/templates.rs +++ b/src/utils/templates.rs @@ -842,11 +842,7 @@ pub async fn load_registry() -> Result { if let Ok(modified) = metadata.modified() { use std::time::{Duration, SystemTime}; let ttl = Duration::from_secs(24 * 60 * 60); // 24 hours - if SystemTime::now() - .duration_since(modified) - .unwrap_or_else(|_| ttl) - < ttl - { + if SystemTime::now().duration_since(modified).unwrap_or(ttl) < ttl { let contents = fs::read_to_string(&cache_path).with_context(|| { format!("Failed to read cached registry at {}", cache_path.display()) })?; @@ -1200,6 +1196,9 @@ pub async fn get_template_by_name_and_version( } } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn semver_cmp(a: &str, b: &str) -> std::cmp::Ordering { let parse_version = |v: &str| { v.strip_prefix('v') @@ -1423,6 +1422,10 @@ pub async fn publish_template( /// Like `publish_template` but also records optional CLI version constraints. /// Install a template from a directory or `.zip` archive into the local registry. +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] pub async fn install_template_package( package_path: &Path, name: String, @@ -1450,6 +1453,10 @@ pub async fn install_template_package( .await } +// Each parameter is an independent, named input (CLI flags / distinct config +// values); bundling them into a struct here would add indirection without +// reducing real complexity. +#[allow(clippy::too_many_arguments)] pub async fn publish_template_versioned( template_path: &Path, name: String, @@ -1501,16 +1508,15 @@ pub async fn publish_template_versioned( copy_dir_recursive(&source_root, &dest)?; let created_at = Utc::now().to_rfc3339(); - let mut changelog: Vec = Vec::new(); - changelog.push(ChangelogEntry { + let changelog = vec![ChangelogEntry { version: version.clone(), date: Utc::now().format("%Y-%m-%d").to_string(), notes: "Initial release".to_string(), - }); + }]; let entry = TemplateEntry { name: name.clone(), - changelog: None, + changelog: Some(changelog), repository: None, security_review: None, version: version.clone(), diff --git a/src/utils/test_automation.rs b/src/utils/test_automation.rs index b3b0ef22..70142e13 100644 --- a/src/utils/test_automation.rs +++ b/src/utils/test_automation.rs @@ -1,9 +1,7 @@ -use anyhow::{Context, Result}; +use anyhow::Result; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use std::fs; use std::path::{Path, PathBuf}; -use std::process::Command; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Instant; @@ -114,7 +112,7 @@ impl TestCaseGenerator { } pub fn generate_from_contract(&self) -> Result { - let wasm_path = self + let _wasm_path = self .contract_path .join("target/wasm32-unknown-unknown/release"); @@ -208,7 +206,7 @@ impl TestCaseGenerator { &self, line: &str, line_num: usize, - file_path: &Path, + _file_path: &Path, ) -> Result { let function_name = line .trim_start() @@ -312,7 +310,7 @@ impl ParallelTestRunner { self.generate_report(suite, results, duration) } - fn run_single_test(test: &TestCase, wasm_path: &Path) -> TestResult { + fn run_single_test(test: &TestCase, _wasm_path: &Path) -> TestResult { let start = Instant::now(); // Simulate test execution diff --git a/src/utils/test_generator.rs b/src/utils/test_generator.rs index d22f7830..3f5aed17 100644 --- a/src/utils/test_generator.rs +++ b/src/utils/test_generator.rs @@ -210,6 +210,9 @@ fn safe_identifier(value: &str) -> String { identifier } +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] fn is_mutating(content: &str, func: &str) -> bool { let mut in_fn = false; for line in content.lines() { diff --git a/src/utils/test_optimizer.rs b/src/utils/test_optimizer.rs index d451cd06..b4d10e2b 100644 --- a/src/utils/test_optimizer.rs +++ b/src/utils/test_optimizer.rs @@ -4,9 +4,6 @@ use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::fs; use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex}; -use std::thread; -use std::time::{Duration, Instant}; // ── Core Data Structures ───────────────────────────────────────────────────── @@ -328,8 +325,8 @@ impl TestOptimizer { .iter() .cloned() .partition(|t| t.resource_profile.io_intensity > 0.6); - let cpu_bound: Vec = vec![]; - let memory_bound: Vec = vec![]; + let _cpu_bound: Vec = vec![]; + let _memory_bound: Vec = vec![]; let general: Vec = vec![]; let (cpu_only, general): (Vec<_>, Vec<_>) = general .into_iter() @@ -419,7 +416,7 @@ impl TestOptimizer { let mut score = stability * 60.0 + transition_ratio * 40.0; - if failure_rate < 0.1 || failure_rate > 0.9 { + if !(0.1..=0.9).contains(&failure_rate) { score *= 0.3; } diff --git a/src/utils/testnet_integration.rs b/src/utils/testnet_integration.rs index bf5ac782..330b087a 100644 --- a/src/utils/testnet_integration.rs +++ b/src/utils/testnet_integration.rs @@ -1,5 +1,4 @@ use anyhow::{Context, Result}; -use reqwest; use serde::{Deserialize, Serialize}; use std::time::{Duration, Instant}; @@ -173,6 +172,9 @@ pub struct LedgerEntryResult { // ── Raw JSON-RPC helpers ─────────────────────────────────────────────────── #[derive(Debug, Serialize)] +// Not currently called from any code path in this crate. Kept rather than +// removed since deleting it is a product decision, not a lint-scoping one. +#[allow(dead_code)] struct RpcRequest<'a> { jsonrpc: &'static str, id: u64, From 3e35425cbb54427ee7198cf3300461b84e5360f5 Mon Sep 17 00:00:00 2001 From: TheWeirdDee Date: Wed, 26 Aug 2026 15:12:09 +0100 Subject: [PATCH 2/6] fix(lint): fix clippy warnings from a newer Rust/Clippy toolchain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI's clippy job pulled Rust/Clippy 1.98.0 (dtolnay/rust-toolchain@stable tracks whatever's current), ~9 minor versions ahead of the 1.89.0 toolchain this branch was originally validated against locally. Clippy periodically adds new lints to its default groups between releases, and clippy::all being removed (previous commit) means every one of those newly-added lints is now live on this crate too. Installed the matching stable toolchain locally and fixed everything it flagged that 1.89 didn't know about: unnecessary_sort_by (7, mostly `.sort_by(|a,b| b.x.cmp(&a.x))` → `.sort_by_key(|a| Reverse(a.x))` after auto-fix declined the ones needing Reverse), manual_checked_ops (2, `if total > 0 {x/total} else {0}` → `x.checked_div(total).unwrap_or(0)`), explicit_counter_loop (a manually-incremented line counter replaced with `(1u32..).zip(lines)`), and unnecessary_unwrap (an `if x.is_some() { x.unwrap() }` replaced with `if let Some(x) = x`). The rest (useless_borrows_in_formatting, collapsible_match, derivable_impls, filter_next, manual_is_multiple_of, useless_format) were machine-applicable via `cargo clippy --fix`. `cargo clippy --all-features --locked -- -D warnings` now passes clean under both the 1.89.0 toolchain this branch started on and the 1.98.0 stable CI actually runs. --- src/commands/collab.rs | 2 +- src/commands/compliance.rs | 22 +++++++++++----------- src/commands/optimize.rs | 2 +- src/main.rs | 8 +++----- src/utils/ai_context.rs | 2 +- src/utils/ai_feedback.rs | 2 +- src/utils/ai_model_router.rs | 6 +++--- src/utils/ai_navigation.rs | 5 ++--- src/utils/ai_property_testing.rs | 2 +- src/utils/ai_rate_limiter.rs | 2 +- src/utils/ai_test_assistant.rs | 4 +--- src/utils/contract_health_monitor.rs | 3 +-- src/utils/contract_versioning.rs | 4 ++-- src/utils/cost_management.rs | 2 +- src/utils/event_monitoring.rs | 2 +- src/utils/migration_ai.rs | 6 ++---- src/utils/simulation_resources.rs | 2 +- src/utils/stream.rs | 9 ++------- src/utils/test_optimizer.rs | 4 ++-- 19 files changed, 38 insertions(+), 51 deletions(-) diff --git a/src/commands/collab.rs b/src/commands/collab.rs index 1fd06bdd..f1dc183f 100644 --- a/src/commands/collab.rs +++ b/src/commands/collab.rs @@ -371,7 +371,7 @@ fn handle_contributions(days: i64) -> Result<()> { let total: usize = counts.values().sum(); let mut rows: Vec<(String, usize)> = counts.into_iter().collect(); - rows.sort_by(|a, b| b.1.cmp(&a.1)); + rows.sort_by_key(|a| std::cmp::Reverse(a.1)); let headers = &["Author", "Commits", "Share"]; let table_rows: Vec> = rows diff --git a/src/commands/compliance.rs b/src/commands/compliance.rs index 1a0755f3..ad86b132 100644 --- a/src/commands/compliance.rs +++ b/src/commands/compliance.rs @@ -200,7 +200,7 @@ fn handle_init() -> Result<()> { enabled_mark, policy.name.white(), severity_color, - &policy.id[..12].cyan() + policy.id[..12].cyan() ); } @@ -250,7 +250,7 @@ fn handle_check(args: CheckArgs) -> Result<()> { println!( " {} {}\n", "Policy Checks".bright_white(), - &format!("({})", report.checks.len()).dimmed() + format!("({})", report.checks.len()).dimmed() ); p::separator(); @@ -275,7 +275,7 @@ fn handle_check(args: CheckArgs) -> Result<()> { println!( " {} {}\n", "Regulatory Checks".bright_white(), - &format!("({})", report.regulatory_checks.len()).dimmed() + format!("({})", report.regulatory_checks.len()).dimmed() ); p::separator(); @@ -307,7 +307,7 @@ fn handle_check(args: CheckArgs) -> Result<()> { println!( " {} {}\n", "Best Practices".bright_white(), - &format!("({})", report.best_practices.len()).dimmed() + format!("({})", report.best_practices.len()).dimmed() ); p::separator(); @@ -428,7 +428,7 @@ fn handle_list_policies() -> Result<()> { let type_str = format!("{:?}", policy.policy_type); println!( " {:<14} {:<36} {:<12} {:<10} {:<8}", - &policy.id[..12].cyan(), + policy.id[..12].cyan(), policy.name.truncate_or_pad(34), type_str, sev, @@ -538,8 +538,8 @@ fn handle_list_reports(args: ListReportsArgs) -> Result<()> { let ts = report.timestamp.get(..16).unwrap_or(&report.timestamp); println!( " {:<14} {:<20} {:<12} {:<8} {:<8} {:<12}", - &report.request_id[..12].cyan(), - &report.contract_id.chars().take(18).collect::(), + report.request_id[..12].cyan(), + report.contract_id.chars().take(18).collect::(), report.network, status, report.blocking_count.to_string().red(), @@ -593,7 +593,7 @@ fn handle_show_report(args: ShowReportArgs) -> Result<()> { println!( " {} {}\n", "Check Results".bright_white(), - &format!("({})", report.checks.len()).dimmed() + format!("({})", report.checks.len()).dimmed() ); p::separator(); @@ -617,7 +617,7 @@ fn handle_show_report(args: ShowReportArgs) -> Result<()> { println!( " {} {}\n", "Regulatory Checks".bright_white(), - &format!("({})", report.regulatory_checks.len()).dimmed() + format!("({})", report.regulatory_checks.len()).dimmed() ); p::separator(); @@ -643,7 +643,7 @@ fn handle_show_report(args: ShowReportArgs) -> Result<()> { println!( " {} {}\n", "Best Practices".bright_white(), - &format!("({})", report.best_practices.len()).dimmed() + format!("({})", report.best_practices.len()).dimmed() ); p::separator(); @@ -862,7 +862,7 @@ fn handle_dashboard() -> Result<()> { println!( " {} {} | {} | {} | blocking: {}", status, - &report.request_id[..12].cyan(), + report.request_id[..12].cyan(), report.network, ts.dimmed(), report.blocking_count.to_string().red(), diff --git a/src/commands/optimize.rs b/src/commands/optimize.rs index c5110c87..0470d66a 100644 --- a/src/commands/optimize.rs +++ b/src/commands/optimize.rs @@ -621,7 +621,7 @@ fn detect_storage_packing(content: &str, file: &str) -> Vec } if fields.len() >= 2 { let mut sorted = fields.clone(); - sorted.sort_by(|a, b| b.1.cmp(&a.1)); + sorted.sort_by_key(|a| std::cmp::Reverse(a.1)); if sorted != fields { suggestions.push(TransformSuggestion { file: file.to_string(), diff --git a/src/main.rs b/src/main.rs index 5b4f859a..01da1ad8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -775,11 +775,9 @@ fn recovery_hints(command: &str, err: &anyhow::Error) -> Vec { hints.push("Analyze a contract: starforge ai-recommend analyze src/lib.rs".into()); hints.push("Scan a project: starforge ai-recommend scan .".into()); } - "benchmark" | "test" => { - if msg.contains("wasm") || msg.contains("not found") { - hints.push("Build your contract first: stellar contract build".into()); - hints.push("Pass the correct --wasm path to the command.".into()); - } + "benchmark" | "test" if (msg.contains("wasm") || msg.contains("not found")) => { + hints.push("Build your contract first: stellar contract build".into()); + hints.push("Pass the correct --wasm path to the command.".into()); } _ => {} diff --git a/src/utils/ai_context.rs b/src/utils/ai_context.rs index eac2511e..d2594d1f 100644 --- a/src/utils/ai_context.rs +++ b/src/utils/ai_context.rs @@ -185,7 +185,7 @@ impl AIContextManager { items.extend(edits); } - items.sort_by(|a, b| b.priority.cmp(&a.priority)); + items.sort_by_key(|a| std::cmp::Reverse(a.priority)); Ok(items) } diff --git a/src/utils/ai_feedback.rs b/src/utils/ai_feedback.rs index 40c67ccd..fb0131b9 100644 --- a/src/utils/ai_feedback.rs +++ b/src/utils/ai_feedback.rs @@ -406,7 +406,7 @@ pub fn get_feature_stats(feature: &str) -> Result { let mut top_corrections: Vec<(CorrectionCategory, usize)> = correction_counts.into_iter().collect(); - top_corrections.sort_by(|a, b| b.1.cmp(&a.1)); + top_corrections.sort_by_key(|a| std::cmp::Reverse(a.1)); top_corrections.truncate(5); let metrics = calculate_quality_metrics(feature)?; diff --git a/src/utils/ai_model_router.rs b/src/utils/ai_model_router.rs index 72f84501..d0c8d639 100644 --- a/src/utils/ai_model_router.rs +++ b/src/utils/ai_model_router.rs @@ -626,15 +626,15 @@ pub fn model_performance_stats(days: Option) -> Result 0 { latency / total } else { 0 }, - avg_tokens: if total > 0 { tokens / total } else { 0 }, + avg_latency_ms: latency.checked_div(total).unwrap_or(0), + avg_tokens: tokens.checked_div(total).unwrap_or(0), total_calls: total, } }, ) .collect(); - stats.sort_by(|a, b| b.total_calls.cmp(&a.total_calls)); + stats.sort_by_key(|a| std::cmp::Reverse(a.total_calls)); Ok(stats) } diff --git a/src/utils/ai_navigation.rs b/src/utils/ai_navigation.rs index 66f1e03d..3ee163ed 100644 --- a/src/utils/ai_navigation.rs +++ b/src/utils/ai_navigation.rs @@ -488,10 +488,9 @@ fn parse_dependencies(root: &Path, file: &Path, source: &str) -> Vec let trimmed = line.trim(); let (kind, rest) = if let Some(rest) = trimmed.strip_prefix("use ") { ("use", rest) - } else if let Some(rest) = trimmed.strip_prefix("mod ") { - ("module", rest) } else { - return None; + let rest = trimmed.strip_prefix("mod ")?; + ("module", rest) }; let target = rest .trim_end_matches(';') diff --git a/src/utils/ai_property_testing.rs b/src/utils/ai_property_testing.rs index 0d9c4370..f77f65fa 100644 --- a/src/utils/ai_property_testing.rs +++ b/src/utils/ai_property_testing.rs @@ -148,7 +148,7 @@ pub fn discover_properties(source_code: &str) -> Result> ), property_type: PropertyType::Postcondition, target_function: Some(func.name.clone()), - invariants: vec![format!("result is not panic")], + invariants: vec!["result is not panic".to_string()], confidence: 0.9, }); } diff --git a/src/utils/ai_rate_limiter.rs b/src/utils/ai_rate_limiter.rs index 2f73955c..e947400c 100644 --- a/src/utils/ai_rate_limiter.rs +++ b/src/utils/ai_rate_limiter.rs @@ -262,7 +262,7 @@ impl AIRateLimiter { } queue.push(request); - queue.sort_by(|a, b| b.priority.cmp(&a.priority)); + queue.sort_by_key(|a| std::cmp::Reverse(a.priority)); metrics.queued_requests += 1; drop(queue); diff --git a/src/utils/ai_test_assistant.rs b/src/utils/ai_test_assistant.rs index 56fce382..2f7544d0 100644 --- a/src/utils/ai_test_assistant.rs +++ b/src/utils/ai_test_assistant.rs @@ -355,11 +355,10 @@ pub struct ParamInfo { fn extract_functions_with_signatures(source: &str) -> Vec { let mut functions = Vec::new(); - let mut current_line = 1u32; let mut in_function = false; let mut brace_depth = 0u32; - for line in source.lines() { + for (current_line, line) in (1u32..).zip(source.lines()) { let trimmed = line.trim(); if !in_function { @@ -378,7 +377,6 @@ fn extract_functions_with_signatures(source: &str) -> Vec { in_function = false; } } - current_line += 1; } functions } diff --git a/src/utils/contract_health_monitor.rs b/src/utils/contract_health_monitor.rs index 88309fa1..663b1fda 100644 --- a/src/utils/contract_health_monitor.rs +++ b/src/utils/contract_health_monitor.rs @@ -134,8 +134,7 @@ fn run_health_probes(contract_id: &str, network: &str) -> Vec { let last_deploy = deploy_history::load_history() .unwrap_or_default() .into_iter() - .filter(|r| r.contract_id.as_deref() == Some(contract_id) && r.network == network) - .next_back(); + .rfind(|r| r.contract_id.as_deref() == Some(contract_id) && r.network == network); let (deploy_status, deploy_msg) = match &last_deploy { Some(r) if r.status == deploy_history::DeployStatus::Success => ( ContractHealthStatus::Healthy, diff --git a/src/utils/contract_versioning.rs b/src/utils/contract_versioning.rs index 3f4aa475..32fbbe5d 100644 --- a/src/utils/contract_versioning.rs +++ b/src/utils/contract_versioning.rs @@ -388,8 +388,8 @@ fn comparator_interval(c: &Comparator) -> Interval { inclusive: true, }), upper: Some(Bound { - value: if c.minor.is_some() { - Version::new(c.major, c.minor.unwrap() + 1, 0) + value: if let Some(minor) = c.minor { + Version::new(c.major, minor + 1, 0) } else { Version::new(c.major + 1, 0, 0) }, diff --git a/src/utils/cost_management.rs b/src/utils/cost_management.rs index 67eb8e93..ad4711ee 100644 --- a/src/utils/cost_management.rs +++ b/src/utils/cost_management.rs @@ -412,7 +412,7 @@ pub fn compare_networks( adjusted_total_xlm: adjusted as f64 / 10_000_000.0, }); } - results.sort_by(|a, b| a.adjusted_total_stroops.cmp(&b.adjusted_total_stroops)); + results.sort_by_key(|a| a.adjusted_total_stroops); Ok(results) } diff --git a/src/utils/event_monitoring.rs b/src/utils/event_monitoring.rs index fe843cc5..a67f333d 100644 --- a/src/utils/event_monitoring.rs +++ b/src/utils/event_monitoring.rs @@ -480,7 +480,7 @@ fn write_counts(out: &mut String, title: &str, counts: &HashMap) } let mut items: Vec<_> = counts.iter().collect(); - items.sort_by(|(left, _), (right, _)| left.cmp(right)); + items.sort_by_key(|(left, _)| *left); for (key, count) in items { let _ = writeln!(out, " - {}: {}", key, count); } diff --git a/src/utils/migration_ai.rs b/src/utils/migration_ai.rs index 49fe49f5..61281732 100644 --- a/src/utils/migration_ai.rs +++ b/src/utils/migration_ai.rs @@ -406,9 +406,8 @@ fn analyze_protocol_upgrade( _suggestions: &mut [MigrationSuggestion], ) { match (config.old_protocol_version, config.new_protocol_version) { - (Some(old), Some(new)) if old != new => { - if new > old { - breaking_changes.push(BreakingChange { + (Some(old), Some(new)) if old != new && new > old => { + breaking_changes.push(BreakingChange { category: "protocol_upgrade".into(), severity: Severity::Major, title: format!("Soroban protocol upgrade: v{} → v{}", old, new), @@ -425,7 +424,6 @@ fn analyze_protocol_upgrade( ), affected_items: vec!["protocol".into(), "host_functions".into()], }); - } } _ => {} } diff --git a/src/utils/simulation_resources.rs b/src/utils/simulation_resources.rs index 1db4fbcc..012f5c2b 100644 --- a/src/utils/simulation_resources.rs +++ b/src/utils/simulation_resources.rs @@ -595,7 +595,7 @@ fn format_thousands(value: u64) -> String { let digits = value.to_string(); let mut out = String::with_capacity(digits.len() + digits.len() / 3); for (i, ch) in digits.chars().enumerate() { - if i > 0 && (digits.len() - i) % 3 == 0 { + if i > 0 && (digits.len() - i).is_multiple_of(3) { out.push(','); } out.push(ch); diff --git a/src/utils/stream.rs b/src/utils/stream.rs index d71ea14d..ba8683cc 100644 --- a/src/utils/stream.rs +++ b/src/utils/stream.rs @@ -18,11 +18,12 @@ pub struct EventStreamFilters { } /// Transport used for Soroban event streaming. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum EventStreamTransport { /// Prefer a persistent WebSocket JSON-RPC connection, then fall back to HTTP polling. Auto, /// Use JSON-RPC over HTTP polling. + #[default] Http, /// Use JSON-RPC over a persistent WebSocket connection. WebSocket, @@ -42,12 +43,6 @@ impl EventStreamTransport { } } -impl Default for EventStreamTransport { - fn default() -> Self { - Self::Http - } -} - pub struct SorobanEventStream { rpc_url: String, websocket_url: String, diff --git a/src/utils/test_optimizer.rs b/src/utils/test_optimizer.rs index b4d10e2b..3ec6a4ea 100644 --- a/src/utils/test_optimizer.rs +++ b/src/utils/test_optimizer.rs @@ -640,7 +640,7 @@ impl TestOptimizer { let avg = total_duration as f64 / results.len() as f64; let mut sorted = results.to_vec(); - sorted.sort_by(|a, b| a.duration_ms.cmp(&b.duration_ms)); + sorted.sort_by_key(|a| a.duration_ms); let median = sorted[sorted.len() / 2].duration_ms as f64; let p95_idx = ((sorted.len() as f64 * 0.95) as usize).min(sorted.len() - 1); @@ -777,7 +777,7 @@ impl TestOptimizer { } }) .collect(); - category_summary.sort_by(|a, b| b.total_failures.cmp(&a.total_failures)); + category_summary.sort_by_key(|a| std::cmp::Reverse(a.total_failures)); let recurrence_ratio = if total_failing > 0 { all_failing From f316e97ec84d2f135f9b0463931d7cd48beed0bc Mon Sep 17 00:00:00 2001 From: TheWeirdDee Date: Wed, 26 Aug 2026 19:05:08 +0100 Subject: [PATCH 3/6] fix(lint): update remaining SkillLevel::from_str call sites in tests/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The should_implement_trait rename (SkillLevel::from_str -> parse_lenient, in the first commit of this branch) only searched src/ for call sites. tests/template_recommendation.rs — a top-level integration test file — had two more, which broke its compilation. Found via CI's deploy-verify job actually attempting to compile it. --- tests/template_recommendation.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/template_recommendation.rs b/tests/template_recommendation.rs index 0adf2bf2..d2971e70 100644 --- a/tests/template_recommendation.rs +++ b/tests/template_recommendation.rs @@ -88,7 +88,7 @@ fn skill_level_parses_all_variants() { ("senior", SkillLevel::Advanced), ] { assert_eq!( - SkillLevel::from_str(input), + SkillLevel::parse_lenient(input), Some(expected), "Expected '{}' to parse correctly", input @@ -100,7 +100,7 @@ fn skill_level_parses_all_variants() { fn skill_level_rejects_unknown_strings() { for bad in ["", "pro", "newbie", "wizard", "123"] { assert_eq!( - SkillLevel::from_str(bad), + SkillLevel::parse_lenient(bad), None, "Expected '{}' to be rejected", bad From 4ea4fba740c0e65755ab1df3e0688523355ae090 Mon Sep 17 00:00:00 2001 From: TheWeirdDee Date: Thu, 27 Aug 2026 12:13:55 +0100 Subject: [PATCH 4/6] fix: repair CI infrastructure and runtime bugs surfaced by full CI run - tests/ai_test_assistant.rs: two of the three generator helpers it exercises (generate_edge_case_descriptions, generate_security_checks, generate_warnings) had been relocated from utils::ai_test_assistant into commands::ai_test as private fns, breaking the integration test. Moved them back to utils::ai_test_assistant as pub fns (their natural home alongside generate_test_priorities) and pointed commands::ai_test at them instead of duplicating the logic. Also added a missing .unwrap() the test needed after analyze_contract_for_testing started returning a Result. - tests/template_recommendation.rs: make_entry() predated the categories/featured/repository_url fields on TemplateEntry. - cargo fmt --all: fixed formatting drift left behind by earlier clippy --fix passes that don't themselves run rustfmt. - deny.toml: removed a stale RUSTSEC-2026-0190 ignore that no longer matches any crate in the dependency tree (cargo-deny treats an unmatched ignore as a failure). - fuzz/Cargo.lock: regenerated; it had drifted out of sync with fuzz/Cargo.toml since it was last committed. - .github/workflows/benchmark-latency.yml: `cargo bench --locked -- cli_cold_start cli_command_latency latency_budget` passed three positional filters, but Criterion's harness only accepts one - combined into a single regex alternation. Also wrapped the PR-comment step in try/catch: fork PRs get a read-only GITHUB_TOKEN and can't post comments, which was failing the whole job even on a successful benchmark run. - templates/registry.json: SecurityReview.findings is Option (consuming code checks `.is_empty()`), but the bundled seed data has always shipped numeric values (0, 1, null), so the CLI's offline fallback path (no cache, no network) has never actually been able to parse its own bundled registry. Converted 0 -> null and 1 -> "1" to match the type, and added a regression test that parses DEFAULT_REGISTRY directly. - src/utils/database.rs: initialize() used `self.get_meta("schema_version").is_ok()` to decide whether a database was already initialized, but get_meta returns Result> - Ok(None) is still Ok. On a genuinely fresh database this took the "already initialized" branch and called run_migrations() against an empty meta table, which fails with "Schema version not found or invalid" - breaking every fresh install. Changed the check to `.is_some()`. - src/utils/test_optimizer.rs: two bugs. 1. batch_tests_by_profile computed the non-IO-bound tests into a variable and then never used it, batching an always-empty placeholder instead - CPU-bound, memory-bound, and general tests were silently dropped from every batch. 2. save_state() didn't create config_dir before writing into it, unlike new(); only worked by accident when the directory happened to already exist. - tests/contract_property_tests.rs: two tests with incorrect premises. prop_magic_header_short_rejected generated 4-7 extra bytes on top of a 4-byte magic header (8-11 bytes total), which is never "short" by validate_wasm's own >= 8 threshold; narrowed the range so the total is always < 8. prop_env_reset_preserves_ledger asserted auth_count() > 0 after only calling auto_approve(), which just registers an allow-list entry and does not itself record an auth attempt; added a require_auth() call so there's actually a record to clear. --- .github/workflows/benchmark-latency.yml | 22 +- deny.toml | 2 - fuzz/Cargo.lock | 351 +++++++++++++-------- src/commands/ai_test.rs | 74 +---- src/plugins/manifest.rs | 15 +- src/utils/ai_test_assistant.rs | 68 ++++ src/utils/database.rs | 6 +- src/utils/templates.rs | 10 + src/utils/test_optimizer.rs | 13 +- templates/registry.json | 20 +- tests/ai_test_assistant.rs | 2 +- tests/bindings_integration.rs | 98 ++++-- tests/bindings_tests.rs | 68 ++-- tests/contract_property_tests.rs | 6 +- tests/plugin_version_compatibility_test.rs | 8 +- tests/template_recommendation.rs | 5 +- 16 files changed, 472 insertions(+), 296 deletions(-) diff --git a/.github/workflows/benchmark-latency.yml b/.github/workflows/benchmark-latency.yml index 04eee48e..233303ff 100644 --- a/.github/workflows/benchmark-latency.yml +++ b/.github/workflows/benchmark-latency.yml @@ -52,9 +52,7 @@ jobs: - name: Run latency benchmarks run: | cargo bench --locked -- \ - cli_cold_start \ - cli_command_latency \ - latency_budget \ + 'cli_cold_start|cli_command_latency|latency_budget' \ 2>&1 | tee target/criterion/latency-bench-output.txt # Parse Criterion output and check against latency budgets. @@ -117,9 +115,15 @@ jobs: } else { summary += '_No latency budget report was generated._'; } - github.rest.issues.createComment({ - issue_number: context.issue.number, - owner: context.repo.owner, - repo: context.repo.repo, - body: summary - }); + try { + await github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: summary + }); + } catch (err) { + // Fork PRs get a read-only GITHUB_TOKEN and can't post comments; + // don't fail the whole job just because the summary couldn't be posted. + core.warning(`Could not post latency budget comment: ${err.message}`); + } diff --git a/deny.toml b/deny.toml index 84ed65a2..5e586f45 100644 --- a/deny.toml +++ b/deny.toml @@ -25,8 +25,6 @@ ignore = [ # rustls-pemfile unmaintained in 1.0.4; reqwest 0.11 requires it. # Superseded in reqwest 0.12, which is a breaking change for this crate. "RUSTSEC-2025-0134", - # anyhow downcast_mut - "RUSTSEC-2026-0190", ] [licenses] diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index e3946d3a..532fb362 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -57,18 +57,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -158,13 +158,13 @@ checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" [[package]] name = "async-trait" -version = "0.1.91" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -316,9 +316,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.4.0" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -516,9 +516,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", ] @@ -711,9 +711,40 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.20", +] [[package]] name = "der" @@ -842,7 +873,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -886,9 +917,9 @@ dependencies = [ [[package]] name = "either" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "encode_unicode" @@ -929,9 +960,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.3.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7" [[package]] name = "escape-bytes" @@ -987,9 +1018,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "flate2" @@ -1018,9 +1049,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -1033,9 +1064,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -1043,15 +1074,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -1060,38 +1091,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.4", ] [[package]] name = "futures-sink" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -1354,9 +1385,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -1368,9 +1399,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -1381,9 +1412,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1395,16 +1426,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -1415,15 +1447,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -1508,9 +1540,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "is_terminal_polyfill" @@ -1533,6 +1565,59 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jiff" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + [[package]] name = "jobserver" version = "0.1.35" @@ -1545,9 +1630,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -1588,9 +1673,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a" dependencies = [ "libc", ] @@ -1614,9 +1699,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "litrs" @@ -1635,9 +1720,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "matchers" @@ -1873,9 +1958,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "polyval" @@ -1891,15 +1976,24 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.14.0" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -1955,9 +2049,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha", @@ -2005,22 +2099,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -2037,9 +2131,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -2163,7 +2257,7 @@ checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "once_cell", "rustls-pki-types", - "rustls-webpki 0.103.13", + "rustls-webpki 0.103.15", "subtle", "zeroize", ] @@ -2198,9 +2292,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -2314,7 +2408,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -2353,9 +2447,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" dependencies = [ "base64 0.22.1", "bs58", @@ -2363,6 +2457,7 @@ dependencies = [ "hex", "indexmap 1.9.3", "indexmap 2.14.0", + "jiff", "schemars 0.9.0", "schemars 1.2.2", "serde_core", @@ -2373,9 +2468,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -2552,6 +2647,7 @@ dependencies = [ "stellar-strkey", "stellar-xdr", "tempfile", + "thiserror 1.0.69", "tokio", "tokio-tungstenite", "toml", @@ -2575,6 +2671,7 @@ dependencies = [ "serde_json", "sha2", "starforge", + "tempfile", ] [[package]] @@ -2639,9 +2736,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -2710,11 +2807,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.19", + "thiserror-impl 2.0.20", ] [[package]] @@ -2730,13 +2827,13 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -2750,9 +2847,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.54" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "num-conv", @@ -2780,9 +2877,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -2828,7 +2925,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.4", ] [[package]] @@ -2940,7 +3037,7 @@ checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", "symlink", - "thiserror 2.0.19", + "thiserror 2.0.20", "time", "tracing-subscriber", ] @@ -3133,9 +3230,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.24.0" +version = "1.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -3178,9 +3275,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -3191,9 +3288,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -3201,9 +3298,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3211,9 +3308,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", @@ -3224,18 +3321,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -3534,9 +3631,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "yoke" @@ -3563,18 +3660,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.55" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.55" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", @@ -3610,9 +3707,9 @@ checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -3621,9 +3718,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -3632,13 +3729,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.4", ] [[package]] diff --git a/src/commands/ai_test.rs b/src/commands/ai_test.rs index c6de77a3..d4f9b8be 100644 --- a/src/commands/ai_test.rs +++ b/src/commands/ai_test.rs @@ -404,8 +404,8 @@ fn generate_locally( description: format!("{} test for {}", test_type_str.replace('_', " "), func.name), code, priority: priority_suggestion.priority.clone(), - edge_cases_covered: generate_edge_case_descriptions(func), - security_checks: generate_security_checks(func), + edge_cases_covered: ata::generate_edge_case_descriptions(func), + security_checks: ata::generate_security_checks(func), }); } } @@ -420,7 +420,7 @@ fn generate_locally( analysis.public_functions ), estimated_coverage_improvement: estimated_improvement, - warnings: generate_warnings(analysis), + warnings: ata::generate_warnings(analysis), }) } @@ -540,56 +540,6 @@ fn generate_assertions(func: &ata::FunctionInfo, test_type: &ata::TestType) -> S } } -fn generate_edge_case_descriptions(func: &ata::FunctionInfo) -> Vec { - let mut cases = Vec::new(); - for param in &func.params { - match param.param_type.as_str() { - t if t.contains("Address") => { - cases.push(format!("Zero address for {}", param.name)); - cases.push(format!("Self-referencing address for {}", param.name)); - cases.push(format!("Contract address for {}", param.name)); - } - t if t.contains("u64") || t.contains("i64") => { - cases.push(format!("Zero value for {}", param.name)); - cases.push(format!("Maximum value for {}", param.name)); - cases.push(format!("Minimum positive value for {}", param.name)); - } - t if t.contains("String") => { - cases.push(format!("Empty string for {}", param.name)); - cases.push(format!("Maximum length string for {}", param.name)); - cases.push(format!("Special characters for {}", param.name)); - } - _ => { - cases.push(format!("Default value for {}", param.name)); - } - } - } - if func.is_mutating { - cases.push("Unauthorized caller".to_string()); - cases.push("Double spend / replay".to_string()); - } - cases -} - -fn generate_security_checks(func: &ata::FunctionInfo) -> Vec { - let mut checks = Vec::new(); - if func.is_mutating { - checks.push("Authorization required for state changes".to_string()); - checks.push("Failed auth must not mutate state".to_string()); - checks.push("Replay protection verified".to_string()); - } - if func - .params - .iter() - .any(|p| p.param_type.contains("i64") || p.param_type.contains("u64")) - { - checks.push("Overflow/underflow protection".to_string()); - checks.push("Negative amount handling".to_string()); - } - checks.push("Input validation".to_string()); - checks -} - fn calculate_estimated_improvement( tests: &[ata::GeneratedTest], analysis: &ata::ContractAnalysis, @@ -603,24 +553,6 @@ fn calculate_estimated_improvement( base_improvement.min(50.0) } -fn generate_warnings(analysis: &ata::ContractAnalysis) -> Vec { - let mut warnings = Vec::new(); - if analysis.complex_functions > 3 { - warnings.push(format!( - "Contract has {} complex functions that may need additional test cases", - analysis.complex_functions - )); - } - if analysis.storage_accesses.len() > 5 { - warnings - .push("Contract has many storage accesses - ensure storage mock coverage".to_string()); - } - if !analysis.external_calls.is_empty() { - warnings.push("Contract makes external calls - consider integration tests".to_string()); - } - warnings -} - fn handle_generate_output( response: &ata::TestGenerationResponse, args: &GenerateArgs, diff --git a/src/plugins/manifest.rs b/src/plugins/manifest.rs index 9602e331..b071dbdc 100644 --- a/src/plugins/manifest.rs +++ b/src/plugins/manifest.rs @@ -35,13 +35,14 @@ impl SupportedVersionPolicy { /// Evaluates compatibility of a plugin manifest against this supported-version policy. pub fn evaluate(&self, manifest: &PluginManifest) -> Result<()> { - let (plugin_major, _, _) = parse_version_parts(&manifest.starforge_version).ok_or_else(|| { - anyhow::anyhow!( - "Invalid 'starforge_version' format in manifest for '{}': '{}'", - manifest.name, - manifest.starforge_version - ) - })?; + let (plugin_major, _, _) = + parse_version_parts(&manifest.starforge_version).ok_or_else(|| { + anyhow::anyhow!( + "Invalid 'starforge_version' format in manifest for '{}': '{}'", + manifest.name, + manifest.starforge_version + ) + })?; if plugin_major != self.supported_major { anyhow::bail!( diff --git a/src/utils/ai_test_assistant.rs b/src/utils/ai_test_assistant.rs index 2f7544d0..a8704ce9 100644 --- a/src/utils/ai_test_assistant.rs +++ b/src/utils/ai_test_assistant.rs @@ -550,6 +550,74 @@ pub fn generate_test_priorities(analysis: &ContractAnalysis) -> Vec Vec { + let mut cases = Vec::new(); + for param in &func.params { + match param.param_type.as_str() { + t if t.contains("Address") => { + cases.push(format!("Zero address for {}", param.name)); + cases.push(format!("Self-referencing address for {}", param.name)); + cases.push(format!("Contract address for {}", param.name)); + } + t if t.contains("u64") || t.contains("i64") => { + cases.push(format!("Zero value for {}", param.name)); + cases.push(format!("Maximum value for {}", param.name)); + cases.push(format!("Minimum positive value for {}", param.name)); + } + t if t.contains("String") => { + cases.push(format!("Empty string for {}", param.name)); + cases.push(format!("Maximum length string for {}", param.name)); + cases.push(format!("Special characters for {}", param.name)); + } + _ => { + cases.push(format!("Default value for {}", param.name)); + } + } + } + if func.is_mutating { + cases.push("Unauthorized caller".to_string()); + cases.push("Double spend / replay".to_string()); + } + cases +} + +pub fn generate_security_checks(func: &FunctionInfo) -> Vec { + let mut checks = Vec::new(); + if func.is_mutating { + checks.push("Authorization required for state changes".to_string()); + checks.push("Failed auth must not mutate state".to_string()); + checks.push("Replay protection verified".to_string()); + } + if func + .params + .iter() + .any(|p| p.param_type.contains("i64") || p.param_type.contains("u64")) + { + checks.push("Overflow/underflow protection".to_string()); + checks.push("Negative amount handling".to_string()); + } + checks.push("Input validation".to_string()); + checks +} + +pub fn generate_warnings(analysis: &ContractAnalysis) -> Vec { + let mut warnings = Vec::new(); + if analysis.complex_functions > 3 { + warnings.push(format!( + "Contract has {} complex functions that may need additional test cases", + analysis.complex_functions + )); + } + if analysis.storage_accesses.len() > 5 { + warnings + .push("Contract has many storage accesses - ensure storage mock coverage".to_string()); + } + if !analysis.external_calls.is_empty() { + warnings.push("Contract makes external calls - consider integration tests".to_string()); + } + warnings +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TestPrioritySuggestion { pub function_name: String, diff --git a/src/utils/database.rs b/src/utils/database.rs index f7b6b408..6739f49e 100644 --- a/src/utils/database.rs +++ b/src/utils/database.rs @@ -21,7 +21,7 @@ pub trait Migration: Send + Sync { /// Apply the migration (upgrade) fn up(&self, conn: &Connection) -> Result<()>; - + /// Rollback the migration (downgrade) fn down(&self, conn: &Connection) -> Result<()>; } @@ -116,7 +116,7 @@ impl Database { self.ensure_column("wallets", "rotation_history", "TEXT NOT NULL DEFAULT '[]'")?; // Run migrations if this is not a fresh database - if self.get_meta("schema_version").is_ok() { + if self.get_meta("schema_version")?.is_some() { self.run_migrations()?; } else { // Fresh database - set initial version @@ -1134,7 +1134,7 @@ impl Migration for MigrationV1 { // This is a no-op since the initial schema is already applied in SCHEMA Ok(()) } - + fn down(&self, conn: &Connection) -> Result<()> { // Rollback: drop all tables conn.execute_batch( diff --git a/src/utils/templates.rs b/src/utils/templates.rs index 6e40c84d..d14742b3 100644 --- a/src/utils/templates.rs +++ b/src/utils/templates.rs @@ -2047,6 +2047,16 @@ pub async fn rollback_installed_template(name: &str) -> Result TemplateEntry { TemplateEntry { name: name.to_string(), diff --git a/src/utils/test_optimizer.rs b/src/utils/test_optimizer.rs index 3ec6a4ea..09b2500b 100644 --- a/src/utils/test_optimizer.rs +++ b/src/utils/test_optimizer.rs @@ -215,6 +215,10 @@ impl TestOptimizer { } fn save_state(&self) -> Result<()> { + if !self.config_dir.exists() { + fs::create_dir_all(&self.config_dir) + .with_context(|| format!("Failed to create {}", self.config_dir.display()))?; + } let history_path = self.config_dir.join("history.json"); fs::write(&history_path, serde_json::to_string_pretty(&self.history)?) .with_context(|| format!("Failed to write {}", history_path.display()))?; @@ -321,17 +325,14 @@ impl TestOptimizer { ) -> Vec> { let mut batches: Vec> = Vec::new(); - let (io_bound, _other): (Vec, Vec) = tests + let (io_bound, other): (Vec, Vec) = tests .iter() .cloned() .partition(|t| t.resource_profile.io_intensity > 0.6); - let _cpu_bound: Vec = vec![]; - let _memory_bound: Vec = vec![]; - let general: Vec = vec![]; - let (cpu_only, general): (Vec<_>, Vec<_>) = general + let (cpu_only, other): (Vec<_>, Vec<_>) = other .into_iter() .partition(|t| t.resource_profile.cpu_intensity > 0.6); - let (mem_only, general): (Vec<_>, Vec<_>) = general + let (mem_only, general): (Vec<_>, Vec<_>) = other .into_iter() .partition(|t| t.resource_profile.memory_mb > 256); diff --git a/templates/registry.json b/templates/registry.json index 2f25471b..5027e308 100644 --- a/templates/registry.json +++ b/templates/registry.json @@ -23,7 +23,7 @@ "status": "audited", "audited_at": "2025-03-15T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 95 }, "changelog": [ @@ -54,7 +54,7 @@ "status": "audited", "audited_at": "2025-04-01T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 1, + "findings": "1", "score": 88 }, "changelog": [ @@ -142,7 +142,7 @@ "status": "audited", "audited_at": "2025-05-01T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 98 }, "changelog": [ @@ -172,7 +172,7 @@ "status": "audited", "audited_at": "2025-02-01T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 96 }, "changelog": [ @@ -200,7 +200,7 @@ "status": "audited", "audited_at": "2025-05-20T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 97 }, "changelog": [ @@ -229,7 +229,7 @@ "status": "audited", "audited_at": "2025-04-10T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 95 }, "changelog": [ @@ -257,7 +257,7 @@ "status": "audited", "audited_at": "2025-04-10T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 96 }, "changelog": [ @@ -285,7 +285,7 @@ "status": "audited", "audited_at": "2025-06-29T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 98 }, "changelog": [ @@ -313,7 +313,7 @@ "status": "audited", "audited_at": "2025-06-29T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 97 }, "changelog": [ @@ -341,7 +341,7 @@ "status": "audited", "audited_at": "2025-06-29T00:00:00Z", "auditor": "StarForge Security Team", - "findings": 0, + "findings": null, "score": 94 }, "changelog": [ diff --git a/tests/ai_test_assistant.rs b/tests/ai_test_assistant.rs index e48a5950..179cb729 100644 --- a/tests/ai_test_assistant.rs +++ b/tests/ai_test_assistant.rs @@ -393,7 +393,7 @@ fn coverage_input_serialization_roundtrip() { #[test] fn empty_contract_analysis() { - let analysis = ata::analyze_contract_for_testing("fn helper() {}"); + let analysis = ata::analyze_contract_for_testing("fn helper() {}").unwrap(); assert_eq!(analysis.total_functions, 0); assert_eq!(analysis.public_functions, 0); } diff --git a/tests/bindings_integration.rs b/tests/bindings_integration.rs index 8a6ff2cf..572cd380 100644 --- a/tests/bindings_integration.rs +++ b/tests/bindings_integration.rs @@ -1,9 +1,9 @@ // Integration test for the binding generator // This test demonstrates a complete workflow with a simple example +use starforge::utils::bindings::BindingLanguage; use std::path::Path; use tempfile::NamedTempFile; -use starforge::utils::bindings::BindingLanguage; /// Test that demonstrates the complete binding generation workflow #[test] @@ -13,7 +13,7 @@ fn test_complete_binding_workflow() { let wasm_bytes = create_example_wasm_with_metadata(); let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), &wasm_bytes).unwrap(); - + // Test each language for lang in [ BindingLanguage::Rust, @@ -22,37 +22,55 @@ fn test_complete_binding_workflow() { BindingLanguage::Go, ] { println!("Testing binding generation for {:?}", lang); - + let result = starforge::utils::bindings::generate_bindings(temp_file.path(), lang); - + // For this test, we just verify that generation doesn't panic // In a real integration test with a proper contract, we would: // 1. Verify the generated code compiles // 2. Test that the generated client can be instantiated // 3. Verify type safety and method signatures - + match result { Ok(code) => { // Basic validation of generated code match lang { BindingLanguage::Rust => { - assert!(code.contains("pub struct ContractClient"), "Missing ContractClient in Rust"); + assert!( + code.contains("pub struct ContractClient"), + "Missing ContractClient in Rust" + ); assert!(code.contains("impl ContractClient"), "Missing impl in Rust"); } BindingLanguage::TypeScript => { - assert!(code.contains("export class ContractClient"), "Missing ContractClient in TS"); - assert!(code.contains("export interface"), "Missing interfaces in TS"); + assert!( + code.contains("export class ContractClient"), + "Missing ContractClient in TS" + ); + assert!( + code.contains("export interface"), + "Missing interfaces in TS" + ); } BindingLanguage::Python => { - assert!(code.contains("class ContractClient"), "Missing ContractClient in Python"); + assert!( + code.contains("class ContractClient"), + "Missing ContractClient in Python" + ); assert!(code.contains("@dataclass"), "Missing dataclass in Python"); } BindingLanguage::Go => { - assert!(code.contains("type ContractClient struct"), "Missing ContractClient in Go"); - assert!(code.contains("func NewContractClient"), "Missing constructor in Go"); + assert!( + code.contains("type ContractClient struct"), + "Missing ContractClient in Go" + ); + assert!( + code.contains("func NewContractClient"), + "Missing constructor in Go" + ); } } - + // Verify event generation (if events were in the metadata) if code.contains("Event") { println!("Generated code includes event definitions for {:?}", lang); @@ -69,25 +87,25 @@ fn test_complete_binding_workflow() { /// Create a minimal WASM with some example contract metadata fn create_example_wasm_with_metadata() -> Vec { let mut wasm = Vec::new(); - + // WASM magic and version wasm.extend(b"\0asm\x01\x00\x00\x00"); - + // For a real test, we would include a proper "contractspecv0" custom section // with XDR-encoded contract metadata. This is simplified for demonstration. - + // Add a custom section header wasm.push(0); // Custom section ID wasm.push(20); // Section length - + // Custom section name "contractspecv0" (simplified) let name = "contractspecv0"; wasm.push(name.len() as u8); wasm.extend(name.as_bytes()); - + // Simplified metadata - in reality this would be XDR-encoded wasm.extend(b"example metadata"); - + wasm } @@ -96,21 +114,27 @@ fn create_example_wasm_with_metadata() -> Vec { fn test_error_handling() { // Test with empty file let temp_file = NamedTempFile::new().unwrap(); - let result = starforge::utils::bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); + let result = + starforge::utils::bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); assert!(result.is_err(), "Should fail on empty file"); - + // Test with non-WASM data let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), b"not wasm at all").unwrap(); - let result = starforge::utils::bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); + let result = + starforge::utils::bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); assert!(result.is_err(), "Should fail on non-WASM data"); - + // Test with valid WASM but no contract metadata let minimal_wasm = b"\0asm\x01\x00\x00\x00"; let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), minimal_wasm).unwrap(); - let result = starforge::utils::bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); - assert!(result.is_err(), "Should fail on WASM without contract metadata"); + let result = + starforge::utils::bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); + assert!( + result.is_err(), + "Should fail on WASM without contract metadata" + ); } /// Test that the binding generator produces idiomatic code for each language @@ -119,23 +143,33 @@ fn test_idiomatic_code_generation() { let test_wasm = create_example_wasm_with_metadata(); let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), &test_wasm).unwrap(); - + // Test each language for basic idiomatic patterns let languages = [ (BindingLanguage::Rust, vec!["pub struct", "impl", "Result<"]), - (BindingLanguage::TypeScript, vec!["export class", "export interface", "type"]), - (BindingLanguage::Python, vec!["class", "def", "from typing import"]), + ( + BindingLanguage::TypeScript, + vec!["export class", "export interface", "type"], + ), + ( + BindingLanguage::Python, + vec!["class", "def", "from typing import"], + ), (BindingLanguage::Go, vec!["type", "func", "package"]), ]; - + for (lang, patterns) in languages { let result = starforge::utils::bindings::generate_bindings(temp_file.path(), lang); - + if let Ok(code) = result { for pattern in &patterns { - assert!(code.contains(pattern), - "Missing pattern '{}' in {:?} generated code", pattern, lang); + assert!( + code.contains(pattern), + "Missing pattern '{}' in {:?} generated code", + pattern, + lang + ); } } } -} \ No newline at end of file +} diff --git a/tests/bindings_tests.rs b/tests/bindings_tests.rs index db6a6209..d7fdcbed 100644 --- a/tests/bindings_tests.rs +++ b/tests/bindings_tests.rs @@ -1,21 +1,21 @@ +use starforge::utils::bindings::{self, BindingLanguage}; use std::path::Path; use tempfile::NamedTempFile; -use starforge::utils::bindings::{self, BindingLanguage}; // Create a minimal valid WASM with contract metadata section for testing fn create_test_wasm() -> Vec { // Create a simple WASM that will fail to parse but is valid structurally // This tests error handling paths let mut wasm = Vec::new(); - + // WASM magic and version wasm.extend(b"\0asm\x01\x00\x00\x00"); - + // Add a type section (minimum valid module) wasm.push(1); // section id for type section wasm.push(1); // section length: 1 byte wasm.push(0); // 0 function types - + wasm } @@ -24,14 +24,20 @@ fn test_generate_rust_bindings() { let test_wasm = create_test_wasm(); let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), &test_wasm).unwrap(); - + let result = bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); // Note: This will fail because our test WASM doesn't have proper contract spec // But we're testing that the function handles it gracefully if result.is_ok() { let generated = result.unwrap(); - assert!(generated.contains("pub struct ContractClient"), "Missing ContractClient struct"); - assert!(generated.contains("impl ContractClient"), "Missing ContractClient implementation"); + assert!( + generated.contains("pub struct ContractClient"), + "Missing ContractClient struct" + ); + assert!( + generated.contains("impl ContractClient"), + "Missing ContractClient implementation" + ); } // Else: expected failure due to invalid spec data } @@ -41,11 +47,14 @@ fn test_generate_typescript_bindings() { let test_wasm = create_test_wasm(); let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), &test_wasm).unwrap(); - + let result = bindings::generate_bindings(temp_file.path(), BindingLanguage::TypeScript); if result.is_ok() { let generated = result.unwrap(); - assert!(generated.contains("export class ContractClient"), "Missing ContractClient class"); + assert!( + generated.contains("export class ContractClient"), + "Missing ContractClient class" + ); assert!(generated.contains("export interface"), "Missing interfaces"); } } @@ -55,12 +64,18 @@ fn test_generate_python_bindings() { let test_wasm = create_test_wasm(); let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), &test_wasm).unwrap(); - + let result = bindings::generate_bindings(temp_file.path(), BindingLanguage::Python); if result.is_ok() { let generated = result.unwrap(); - assert!(generated.contains("class ContractClient"), "Missing ContractClient class"); - assert!(generated.contains("@dataclass"), "Missing dataclass decorators"); + assert!( + generated.contains("class ContractClient"), + "Missing ContractClient class" + ); + assert!( + generated.contains("@dataclass"), + "Missing dataclass decorators" + ); } } @@ -69,12 +84,18 @@ fn test_generate_go_bindings() { let test_wasm = create_test_wasm(); let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), &test_wasm).unwrap(); - + let result = bindings::generate_bindings(temp_file.path(), BindingLanguage::Go); if result.is_ok() { let generated = result.unwrap(); - assert!(generated.contains("type ContractClient struct"), "Missing ContractClient struct"); - assert!(generated.contains("func NewContractClient"), "Missing constructor"); + assert!( + generated.contains("type ContractClient struct"), + "Missing ContractClient struct" + ); + assert!( + generated.contains("func NewContractClient"), + "Missing constructor" + ); } } @@ -83,11 +104,11 @@ fn test_all_languages() { let test_wasm = create_test_wasm(); let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), &test_wasm).unwrap(); - + // Test each language for lang in [ BindingLanguage::Rust, - BindingLanguage::TypeScript, + BindingLanguage::TypeScript, BindingLanguage::Python, BindingLanguage::Go, ] { @@ -102,9 +123,12 @@ fn test_empty_wasm_error() { let empty_wasm = b"\0asm\x01\x00\x00\x00"; // Minimal valid WASM header let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), empty_wasm).unwrap(); - + let result = bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); - assert!(result.is_err(), "Should fail on WASM without contract metadata"); + assert!( + result.is_err(), + "Should fail on WASM without contract metadata" + ); } #[test] @@ -112,7 +136,7 @@ fn test_invalid_wasm_error() { let invalid_data = b"not wasm at all"; let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), invalid_data).unwrap(); - + let result = bindings::generate_bindings(temp_file.path(), BindingLanguage::Rust); assert!(result.is_err(), "Should fail on invalid WASM"); } @@ -124,7 +148,7 @@ fn test_event_generation() { let test_wasm = create_test_wasm(); let temp_file = NamedTempFile::new().unwrap(); std::fs::write(temp_file.path(), &test_wasm).unwrap(); - + // Test each language for event generation for lang in [ BindingLanguage::Rust, @@ -136,4 +160,4 @@ fn test_event_generation() { // The generation should handle missing event data gracefully assert!(result.is_err() || result.is_ok()); } -} \ No newline at end of file +} diff --git a/tests/contract_property_tests.rs b/tests/contract_property_tests.rs index b1fd46d1..11848977 100644 --- a/tests/contract_property_tests.rs +++ b/tests/contract_property_tests.rs @@ -33,7 +33,7 @@ proptest! { /// Any byte sequence with a valid 4-byte magic header but < 8 bytes is rejected. #[test] - fn prop_magic_header_short_rejected(data in prop::collection::vec(any::(), 4..8)) { + fn prop_magic_header_short_rejected(data in prop::collection::vec(any::(), 0..4)) { let mut input = b"\0asm".to_vec(); input.extend(data); let result = validate_wasm(&input); @@ -287,7 +287,9 @@ proptest! { vec![serde_json::json!("test")], serde_json::json!({"data": 1}), ); - env.auth.auto_approve(MockAddress::account(1)); + let account = MockAddress::account(1); + env.auth.auto_approve(account.clone()); + env.auth.require_auth(&account, &MockAddress::contract(1), "test_fn"); prop_assert!(!env.storage.is_empty()); prop_assert!(!env.events.is_empty()); diff --git a/tests/plugin_version_compatibility_test.rs b/tests/plugin_version_compatibility_test.rs index 2219a016..c29a24ba 100644 --- a/tests/plugin_version_compatibility_test.rs +++ b/tests/plugin_version_compatibility_test.rs @@ -1,7 +1,7 @@ use starforge::plugins::interface::CORE_VERSION; use starforge::plugins::manifest::{ - load_manifest_for_library, require_compatible_manifest, PluginManifest, - SupportedVersionPolicy, MANIFEST_FILENAME, + load_manifest_for_library, require_compatible_manifest, PluginManifest, SupportedVersionPolicy, + MANIFEST_FILENAME, }; #[test] @@ -160,7 +160,9 @@ fn test_load_manifest_and_require_manifest_file_system() { // Absent manifest fails require_compatible_manifest let missing_err = require_compatible_manifest(&lib_path, "test-plugin").unwrap_err(); - assert!(missing_err.to_string().contains("Plugin manifest not found")); + assert!(missing_err + .to_string() + .contains("Plugin manifest not found")); // Write valid manifest std::fs::write( diff --git a/tests/template_recommendation.rs b/tests/template_recommendation.rs index d2971e70..62a52e70 100644 --- a/tests/template_recommendation.rs +++ b/tests/template_recommendation.rs @@ -47,7 +47,10 @@ fn make_entry(name: &str, tags: &[&str], downloads: u32, verified: bool) -> Temp homepage: None, documentation: None, security_review: None, - changelog: vec![], + changelog: None, + repository_url: None, + categories: vec![], + featured: false, } } From 9a9f9353176fbc907aba1083b168749b4f63b0d1 Mon Sep 17 00:00:00 2001 From: TheWeirdDee Date: Thu, 27 Aug 2026 12:14:25 +0100 Subject: [PATCH 5/6] fix: repair pre-existing test-compile errors also present on this branch Same underlying crisis already fixed on fix/684-archive-path-traversal (commit e7abe69) - this branch was rebased from upstream/master directly and never received it. Cherry-picked here since the bugs are identical: bindings.rs duplicate read_spec_entries, audit.rs duplicate ci_passed field, compliance.rs missing PartialEq, ai.rs non-mut binding, templates.rs/template_analytics.rs/template_recommender.rs TemplateEntry test literals missing recently-added fields, and plugins/registry.rs missing the description field and resolve_plugin_description/plugin_list_entries helpers (also wired into commands/plugin.rs, fixing the always-blank Description column in `starforge plugin list`). --- src/commands/audit.rs | 1 - src/commands/plugin.rs | 26 ++++++++-------- src/plugins/registry.rs | 49 +++++++++++++++++++++++++++++++ src/utils/ai.rs | 2 +- src/utils/bindings.rs | 16 ---------- src/utils/compliance.rs | 2 +- src/utils/template_analytics.rs | 7 +++-- src/utils/template_recommender.rs | 5 +++- src/utils/templates.rs | 12 ++++++++ 9 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/commands/audit.rs b/src/commands/audit.rs index 773a512f..fcff21f2 100644 --- a/src/commands/audit.rs +++ b/src/commands/audit.rs @@ -502,7 +502,6 @@ mod tests { low: 0, info: 0, }, - ci_passed: true, }; let html = render_html_report(&result); assert!(html.contains("75.0/100")); diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 58273a3c..a15d1a67 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -273,6 +273,7 @@ fn list(json: bool) -> Result<()> { version: String, trust: String, source: String, + description: String, commands: Vec, } @@ -282,20 +283,20 @@ fn list(json: bool) -> Result<()> { description: String, } - let plugins: Vec = reg - .plugins - .iter() + let plugins: Vec = registry::plugin_list_entries(®) + .into_iter() .map(|entry| PluginSummary { - name: entry.name.clone(), - version: entry.plugin_version.clone(), + name: entry.name, + version: entry.plugin_version, trust: entry.trust.label().to_string(), - source: entry.source.clone(), + source: entry.source, + description: entry.description, commands: entry .commands - .iter() + .into_iter() .map(|cmd| PluginCommandSummary { - name: cmd.name.clone(), - description: cmd.description.clone(), + name: cmd.name, + description: cmd.description, }) .collect(), }) @@ -316,21 +317,22 @@ fn list(json: bool) -> Result<()> { p::kv("StarForge core version", CORE_VERSION); p::separator(); - let entries = reg.plugins.clone(); + let list_entries = registry::plugin_list_entries(®); - let plugin_rows: Vec> = entries + let plugin_rows: Vec> = list_entries .iter() .map(|entry| { vec![ entry.name.clone(), entry.plugin_version.clone(), entry.trust.label().to_string(), - "".to_string(), + entry.description.clone(), ] }) .collect(); p::table(&["Name", "Version", "Trust", "Description"], &plugin_rows); + let entries = reg.plugins.clone(); let command_rows: Vec> = entries .iter() .flat_map(|entry| { diff --git a/src/plugins/registry.rs b/src/plugins/registry.rs index bcc30557..5fc8f03f 100644 --- a/src/plugins/registry.rs +++ b/src/plugins/registry.rs @@ -226,6 +226,12 @@ pub struct InstalledPlugin { /// Commands this plugin registers. #[serde(default)] pub commands: Vec, + /// Human-readable description from the plugin manifest, if any. Older + /// registry entries (installed before this field existed) default to + /// empty; use [`resolve_plugin_description`] to get a display-ready + /// value that falls back to the first command's description. + #[serde(default)] + pub description: String, } fn registry_path() -> Result { @@ -326,12 +332,55 @@ pub fn install_plugin( plugin_version: plugin_version.to_string(), installed_at: Some(now), commands, + description: description.to_string(), }); reg.plugins.sort_by(|a, b| a.name.cmp(&b.name)); save_registry(®)?; Ok(()) } +/// Resolve a display-ready description for a plugin: prefers the explicit +/// registry-recorded description, and falls back to the first registered +/// command's description when that's empty (e.g. for plugins installed +/// before `description` was tracked). +pub fn resolve_plugin_description(plugin: &InstalledPlugin) -> String { + if !plugin.description.is_empty() { + return plugin.description.clone(); + } + plugin + .commands + .first() + .map(|cmd| cmd.description.clone()) + .unwrap_or_default() +} + +/// A plugin entry with its description pre-resolved, for listing UIs. +#[derive(Debug, Clone)] +pub struct PluginListEntry { + pub name: String, + pub plugin_version: String, + pub trust: TrustLevel, + pub source: String, + pub description: String, + pub commands: Vec, +} + +/// Build display-ready entries for every installed plugin, with descriptions +/// resolved via [`resolve_plugin_description`]. +pub fn plugin_list_entries(reg: &PluginRegistry) -> Vec { + reg.plugins + .iter() + .map(|p| PluginListEntry { + name: p.name.clone(), + plugin_version: p.plugin_version.clone(), + trust: p.trust.clone(), + source: p.source.clone(), + description: resolve_plugin_description(p), + commands: p.commands.clone(), + }) + .collect() +} + /// Return all commands registered across all installed plugins (read from registry, no .so load). pub fn load_all_registered_commands() -> Vec { load_registry() diff --git a/src/utils/ai.rs b/src/utils/ai.rs index 43fe8dce..d49bfc6b 100644 --- a/src/utils/ai.rs +++ b/src/utils/ai.rs @@ -743,7 +743,7 @@ mod tests { #[test] fn test_circuit_breaker_starts_closed() { - let cb = CircuitBreaker::new(3, 60); + let mut cb = CircuitBreaker::new(3, 60); assert!(cb.is_available()); } diff --git a/src/utils/bindings.rs b/src/utils/bindings.rs index cdc2f160..f2f148a8 100644 --- a/src/utils/bindings.rs +++ b/src/utils/bindings.rs @@ -83,22 +83,6 @@ pub fn generate_bindings(wasm_path: &Path, language: BindingLanguage) -> Result< } } -#[cfg(test)] -pub fn read_spec_entries(wasm: &[u8]) -> Result> { - let spec = contract_spec_section(wasm)?; - let cursor = Cursor::new(spec); - let entries = ScSpecEntry::read_xdr_iter(&mut Limited::new( - cursor, - Limits { - depth: 500, - len: 0x1000000, - }, - )) - .collect::, _>>() - .context("Failed to decode contractspecv0 XDR metadata")?; - Ok(entries) -} - fn read_spec_entries(wasm: &[u8]) -> Result> { let spec = contract_spec_section(wasm)?; let cursor = Cursor::new(spec); diff --git a/src/utils/compliance.rs b/src/utils/compliance.rs index a22062d5..3cb26ab2 100644 --- a/src/utils/compliance.rs +++ b/src/utils/compliance.rs @@ -9,7 +9,7 @@ use std::path::PathBuf; // Severity / Status helpers // ──────────────────────────────────────────────── -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum ComplianceSeverity { Info, Warning, diff --git a/src/utils/template_analytics.rs b/src/utils/template_analytics.rs index 2383e4d1..e99bc492 100644 --- a/src/utils/template_analytics.rs +++ b/src/utils/template_analytics.rs @@ -925,7 +925,10 @@ mod tests { homepage: None, documentation: None, security_review: None, - changelog: vec![], + changelog: None, + repository_url: None, + categories: vec![], + featured: false, } } @@ -1256,7 +1259,7 @@ mod tests { status: "audited".to_string(), audited_at: Some("2026-01-01".to_string()), auditor: Some("Auditor".to_string()), - findings: Some(2), + findings: Some(2.to_string()), score: Some(80.0), }); e.documented = true; diff --git a/src/utils/template_recommender.rs b/src/utils/template_recommender.rs index 42873135..74d5cc8a 100644 --- a/src/utils/template_recommender.rs +++ b/src/utils/template_recommender.rs @@ -520,7 +520,10 @@ mod tests { homepage: None, documentation: None, security_review: None, - changelog: vec![], + changelog: None, + repository_url: None, + categories: vec![], + featured: false, } } diff --git a/src/utils/templates.rs b/src/utils/templates.rs index d14742b3..1ad13c42 100644 --- a/src/utils/templates.rs +++ b/src/utils/templates.rs @@ -2083,6 +2083,9 @@ mod tests { documentation: None, categories: Vec::new(), featured: false, + changelog: None, + repository: None, + security_review: None, } } @@ -2417,6 +2420,9 @@ mod tests { documentation: None, categories: Vec::new(), featured: false, + changelog: None, + repository: None, + security_review: None, }); // Test name search @@ -2468,6 +2474,9 @@ mod tests { documentation: None, categories: Vec::new(), featured: false, + changelog: None, + repository: None, + security_review: None, }; let dest = tmp.path().join(&entry.name); @@ -2521,6 +2530,9 @@ mod tests { documentation: None, categories: Vec::new(), featured: false, + changelog: None, + repository: None, + security_review: None, } } From 1b5a343b790a818112d87d1484c748f909617e59 Mon Sep 17 00:00:00 2001 From: TheWeirdDee Date: Thu, 27 Aug 2026 12:52:47 +0100 Subject: [PATCH 6/6] fix: repair test-only imports and a param name broken by earlier clippy --fix An earlier automated clippy --fix pass on this branch removed imports and renamed a parameter it saw as unused in non-test analysis, but each was genuinely used by #[cfg(test)] code (or, for plugins/registry.rs, became used again once the crisis-fix cherry-pick restored the code that reads it): - commands/feature_flags_cmd.rs: MetricKind, used only in the test module's roundtrip test. - utils/feature_flags.rs: BTreeMap, used only in one dry-run test. - utils/network_simulator/simulator.rs: FailureMode, used only in a failure-injection test. - utils/security/ai_audit_service.rs: AuditLevel, used only in the AuditRequest construction tests. - plugins/registry.rs: install_plugin's description param had been renamed to _description (unused-marker) before the crisis-fix cherry -pick added the line that stores it; renamed back now that it's genuinely read. Scoped each import into its #[cfg(test)] mod rather than restoring it at the top level, so it doesn't reintroduce an unused-import warning in non-test builds. cargo test --lib --all-features --no-run and cargo clippy --all-features --locked -- -D warnings both pass clean. --- src/commands/feature_flags_cmd.rs | 1 + src/plugins/registry.rs | 2 +- src/utils/feature_flags.rs | 1 + src/utils/network_simulator/simulator.rs | 1 + src/utils/security/ai_audit_service.rs | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/commands/feature_flags_cmd.rs b/src/commands/feature_flags_cmd.rs index 0b977af9..ea82c843 100644 --- a/src/commands/feature_flags_cmd.rs +++ b/src/commands/feature_flags_cmd.rs @@ -964,6 +964,7 @@ fn format_rule(rule: &SegmentRule) -> String { #[cfg(test)] mod tests { use super::*; + use crate::utils::feature_flags::MetricKind; #[test] fn format_rule_user_in_list() { diff --git a/src/plugins/registry.rs b/src/plugins/registry.rs index 5fc8f03f..65e5c911 100644 --- a/src/plugins/registry.rs +++ b/src/plugins/registry.rs @@ -311,7 +311,7 @@ pub fn install_plugin( source: &str, starforge_version: &str, plugin_version: &str, - _description: &str, + description: &str, commands: Vec, ) -> Result<()> { if !library_path.exists() { diff --git a/src/utils/feature_flags.rs b/src/utils/feature_flags.rs index e7b320f0..da696f3f 100644 --- a/src/utils/feature_flags.rs +++ b/src/utils/feature_flags.rs @@ -1392,6 +1392,7 @@ pub fn load_or_create_install_id(db: &Database) -> Result { #[cfg(test)] mod tests { use super::*; + use std::collections::BTreeMap; fn db() -> Database { Database::open_in_memory().unwrap() diff --git a/src/utils/network_simulator/simulator.rs b/src/utils/network_simulator/simulator.rs index 43000d64..469054cb 100644 --- a/src/utils/network_simulator/simulator.rs +++ b/src/utils/network_simulator/simulator.rs @@ -737,6 +737,7 @@ impl Default for NetworkSimulator { #[cfg(test)] mod tests { use super::*; + use crate::utils::network_simulator::failure::FailureMode; #[test] fn new_simulator_has_default_state() { diff --git a/src/utils/security/ai_audit_service.rs b/src/utils/security/ai_audit_service.rs index 5ec19209..d44c7c0c 100644 --- a/src/utils/security/ai_audit_service.rs +++ b/src/utils/security/ai_audit_service.rs @@ -251,6 +251,7 @@ fn classify_claude_error(err: &anyhow::Error) -> &'static str { #[cfg(test)] mod tests { use super::*; + use super::super::ai_audit::AuditLevel; #[test] fn test_validate_empty_contract_code() {