Skip to content

Conversation

@QaidVoid
Copy link
Member

@QaidVoid QaidVoid commented Dec 17, 2025

Summary by CodeRabbit

  • New Features

    • Added a dedicated registry crate exposing metadata APIs, package and nest types, and richer error handling; supports SQLite, zstd and JSON metadata formats.
  • Refactor

    • Moved metadata handling out of core into the new registry component; core now relies on registry types.
  • Chores

    • Updated workspace and crate dependencies to include the new registry.
  • Breaking Changes

    • Core no longer exposes prior metadata module/constants; consumers should use the new registry APIs.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 17, 2025

Warning

Rate limit exceeded

@QaidVoid has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 4 minutes and 0 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between a70ba44 and 0aec214.

📒 Files selected for processing (1)
  • crates/soar-db/src/migration.rs (2 hunks)

Walkthrough

New crate soar-registry was added and metadata fetching, processing, and related types (Nest, RemotePackage, magic constants, errors) were moved from soar-core into it; dependent crates were updated to import types/functions from soar-registry, and metadata handling now returns (ETag, MetadataContent) with SQLite or JSON payloads.

Changes

Cohort / File(s) Summary
Workspace & top-level manifests
Cargo.toml, soar-cli/Cargo.toml, soar-core/Cargo.toml
Added crates/soar-registry as a workspace member and dependency; added soar-registry dependency to soar-cli and soar-core; added zstd = "0.13.3" at workspace level.
New crate manifest & root
crates/soar-registry/Cargo.toml, crates/soar-registry/src/lib.rs
Introduced soar-registry crate with modules error, metadata, nest, package; re-exported errors, metadata APIs, Nest, and RemotePackage.
Registry errors
crates/soar-registry/src/error.rs
Added RegistryError enum, pub type Result<T>, and ErrorContext<T> trait with impl for std::io::Result<T> plus diagnostics and tests.
Metadata implementation (moved)
crates/soar-registry/src/metadata.rs
Implemented metadata fetch functions (fetch_metadata, fetch_nest_metadata, fetch_public_key), ETag-aware HTTP handling, content detection (SQLite/Zstd/JSON), decompression, process_metadata_content, and write_metadata_db; exported SQLITE_MAGIC_BYTES, ZST_MAGIC_BYTES, MetadataContent.
Data types (new)
crates/soar-registry/src/nest.rs, crates/soar-registry/src/package.rs
Added Nest struct and RemotePackage with flexible deserialization helpers and tests.
soar-cli updates
soar-cli/src/nest.rs, soar-cli/src/state.rs
Switched imports to soar_registry::Nest and to soar_registry metadata APIs; added handle_json_metadata and updated sync logic to accept (etag, MetadataContent), writing SQLite bytes or populating DB from JSON.
soar-core removals & imports
soar-core/src/constants.rs, soar-core/src/lib.rs, soar-core/src/metadata.rs
Removed SQLITE_MAGIC_BYTES/ZST_MAGIC_BYTES, removed pub mod metadata, and deleted src/metadata.rs (functionality moved to soar-registry).
soar-core type relocations
soar-core/src/database/models.rs, soar-core/src/database/connection.rs, soar-core/src/database/repository.rs, soar-core/src/database/nests/models.rs, soar-core/src/database/nests/repository.rs
Removed local RemotePackage and Nest definitions; replaced usages/imports with soar_registry::RemotePackage and soar_registry::Nest.
DB migration minor change
crates/soar-db/src/migration.rs
apply_migrations now returns Err(e) instead of Err(e.into()) on failure.

Sequence Diagram(s)

mermaid
sequenceDiagram
autonumber
participant CLI as soar-cli
participant Core as soar-core
participant Reg as soar-registry
participant Net as Remote HTTP
participant FS as Filesystem/DB

CLI->>Core: trigger metadata sync (nest/repo)
Core->>Reg: fetch_nest_metadata / fetch_metadata(force)
Reg->>Net: HTTP GET (If-None-Match with ETag)
alt 304 Not Modified
    Net-->>Reg: 304 Not Modified
    Reg-->>Core: None (no update)
else 200 OK
    Net-->>Reg: payload (+ ETag)
    Reg->>Reg: detect content (SQLite / Zstd / JSON)
    alt SQLite bytes
        Reg->>FS: write_metadata_db(sqlite_bytes)
        Reg-->>Core: (etag, SqliteDb)
    else JSON list
        Reg-->>Core: (etag, Json)
        Core->>FS: handle_json_metadata -> populate metadata DB
    end
end
Core-->>CLI: sync result / updated repo state

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on: crates/soar-registry/src/metadata.rs (ETag, HTTP, decompression, temp-file paths), crates/soar-registry/src/package.rs (deserialization edge cases), and soar-cli/src/state.rs (new flow and DB population).
  • Verify all imports updated to soar_registry and removed soar-core metadata symbols no longer referenced.
  • Confirm error conversions and public API re-exports behave consistently across crate boundaries.

Poem

🐰
A crate hopped in with eager cheer,
Carrying nests and packages near,
SQLite bytes or JSON song,
ETags hum and downloads strong.
Hooray — the registry's here!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: initializing a new soar-registry crate with its complete module structure, public API, and integration into the workspace.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Dec 17, 2025

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
soar-core/src/database/nests/models.rs (1)

9-13: Potential panic if name lacks "nest-" prefix.

strip_prefix returns None when the prefix is absent, causing .unwrap() to panic. If the database ever contains a name without this prefix (corruption, migration issue, or manual edit), this will crash.

Consider using unwrap_or or returning the original name:

             name: row
                 .get::<_, String>("name")?
                 .strip_prefix("nest-")
-                .unwrap()
+                .unwrap_or_else(|| row.get::<_, String>("name").unwrap_or_default().as_str())
                 .to_string(),

Or more cleanly:

name: {
    let raw = row.get::<_, String>("name")?;
    raw.strip_prefix("nest-").unwrap_or(&raw).to_string()
}
🧹 Nitpick comments (6)
crates/soar-registry/src/error.rs (2)

26-31: Consider using Display formatting instead of Debug for UreqError.

The {0:?} Debug formatting may expose internal implementation details of the ureq library. Using {0} (Display) would provide cleaner, more user-friendly error messages.

Apply this diff:

-    #[error("HTTP request error: {0:?}")]
+    #[error("HTTP request error: {0}")]
     #[diagnostic(
         code(soar_registry::http),
         help("Check your network connection and the repository URL")
     )]
     UreqError(#[from] ureq::Error),

1-124: Well-structured error handling module.

The error module is well-designed with comprehensive error variants, diagnostic codes, and helpful error messages. The ErrorContext trait provides a clean pattern for enriching IO errors with context.

Consider expanding test coverage to include more error variants when time permits. The current coverage (3/10 variants) provides a foundation but could be more comprehensive.

soar-cli/src/nest.rs (1)

9-23: Consider whether async is necessary here.

This function and others in this file are marked async but don't contain any .await calls. If all database operations are synchronous, the async keyword adds overhead without benefit.

If these are intentionally async for API consistency or planned future async database operations, this is fine to keep.

crates/soar-registry/src/package.rs (2)

26-35: Consider parsing directly as u64 to avoid unnecessary conversion.

The current implementation parses as i64, filters negatives, then converts to u64. This adds overhead for a straightforward unsigned number parse.

Apply this diff to simplify the parsing:

 fn optional_number<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
 where
     D: Deserializer<'de>,
 {
     let s: Option<String> = Option::deserialize(deserializer)?;
     Ok(s.filter(|s| !s.is_empty())
-        .and_then(|s| s.parse::<i64>().ok())
-        .filter(|&n| n >= 0)
-        .map(|n| n as u64))
+        .and_then(|s| s.parse::<u64>().ok()))
 }

234-270: Consider adding tests for edge cases.

The current tests validate basic functionality, but additional test coverage for edge cases would improve robustness:

  • Empty string handling in empty_is_none
  • Invalid number strings in optional_number
  • Invalid boolean strings in flexible_bool
  • Field aliases deserialization
crates/soar-registry/src/metadata.rs (1)

364-410: Consider using RAII for temporary file cleanup.

The temporary file cleanup (lines 392-393, 400-401) is only performed in the success paths. If an error occurs during zstd decoding (line 379-380) or JSON parsing (line 399), the temporary file will be left behind, potentially accumulating over time.

Consider ensuring cleanup happens even on error:

// After creating the temp file
let tmp_path = format!("{}.part", metadata_db_path.display());
let _cleanup = scopeguard::guard(tmp_path.clone(), |path| {
    let _ = fs::remove_file(path);
});

// ... rest of the processing

Or structure the code to handle cleanup in a single location using Rust's drop semantics.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 135af26 and d7e4279.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • Cargo.toml (3 hunks)
  • crates/soar-registry/Cargo.toml (1 hunks)
  • crates/soar-registry/src/error.rs (1 hunks)
  • crates/soar-registry/src/lib.rs (1 hunks)
  • crates/soar-registry/src/metadata.rs (1 hunks)
  • crates/soar-registry/src/nest.rs (1 hunks)
  • crates/soar-registry/src/package.rs (1 hunks)
  • soar-cli/Cargo.toml (1 hunks)
  • soar-cli/src/nest.rs (1 hunks)
  • soar-cli/src/state.rs (7 hunks)
  • soar-core/Cargo.toml (1 hunks)
  • soar-core/src/constants.rs (0 hunks)
  • soar-core/src/database/connection.rs (1 hunks)
  • soar-core/src/database/models.rs (1 hunks)
  • soar-core/src/database/nests/models.rs (1 hunks)
  • soar-core/src/database/nests/repository.rs (1 hunks)
  • soar-core/src/database/repository.rs (1 hunks)
  • soar-core/src/lib.rs (0 hunks)
  • soar-core/src/metadata.rs (0 hunks)
💤 Files with no reviewable changes (3)
  • soar-core/src/constants.rs
  • soar-core/src/metadata.rs
  • soar-core/src/lib.rs
🧰 Additional context used
🧬 Code graph analysis (3)
crates/soar-registry/src/nest.rs (3)
soar-cli/src/state.rs (1)
  • new (67-77)
soar-core/src/database/connection.rs (1)
  • new (20-27)
soar-core/src/database/repository.rs (1)
  • new (15-21)
crates/soar-registry/src/error.rs (3)
soar-cli/src/download.rs (1)
  • download (67-89)
crates/soar-dl/src/http.rs (1)
  • json (72-79)
crates/soar-db/src/models/types.rs (1)
  • serde_json (20-20)
soar-cli/src/state.rs (2)
crates/soar-registry/src/metadata.rs (3)
  • fetch_metadata (243-321)
  • fetch_nest_metadata (88-166)
  • write_metadata_db (435-444)
crates/soar-registry/src/nest.rs (1)
  • new (37-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test
  • GitHub Check: coverage
🔇 Additional comments (28)
soar-core/Cargo.toml (1)

23-23: LGTM! Clean integration of the new registry crate.

The workspace dependency addition is straightforward and consistent with the existing dependency management pattern.

Cargo.toml (3)

7-7: LGTM! New workspace member added correctly.


47-47: LGTM! Workspace dependency path is correct.


57-57: zstd version 0.13.3 is current and secure.

Verification confirms that 0.13.3 is the latest stable version, and no security advisories are registered for this crate. No further action is required.

soar-cli/Cargo.toml (1)

37-37: LGTM! Consistent workspace dependency addition.

soar-core/src/database/repository.rs (1)

4-6: LGTM! RemotePackage import migration is consistent.

The refactoring maintains the same functionality while using the external soar_registry::RemotePackage type.

crates/soar-registry/Cargo.toml (1)

1-25: LGTM! Well-structured crate configuration.

The package metadata and workspace dependencies are properly configured for the new registry crate. The dependency set is appropriate for handling registry operations (HTTP requests, JSON parsing, error handling, compression).

soar-core/src/database/connection.rs (1)

8-10: LGTM! Clean migration to external RemotePackage type.

The import refactoring from the local module to soar_registry::RemotePackage is clean and maintains backward compatibility with the existing API. Verification confirms all RemotePackage references have been consistently migrated—no legacy imports remain, and the new import pattern is used uniformly across the codebase.

soar-core/src/database/nests/repository.rs (1)

2-2: Nest type migration is correct and consistent.

The import refactoring to use soar_registry::Nest aligns with the overall migration strategy. Verified that all Nest type references have been migrated consistently across the codebase with no remaining old import patterns or conflicting definitions.

crates/soar-registry/src/nest.rs (1)

1-40: Well-structured data type with clear documentation.

The Nest struct is well-documented with clear explanations of URL formats and field purposes. The derives are appropriate for serialization needs.

soar-cli/src/nest.rs (1)

2-7: Import refactoring looks correct.

The import path for Nest has been correctly updated to use soar_registry::Nest, aligning with the type extraction to the new crate.

soar-core/src/database/models.rs (1)

4-4: Clean import simplification.

The serde import correctly removes the unused de and Deserializer modules now that RemotePackage and its custom deserialization helpers have been moved to soar-registry.

crates/soar-registry/src/lib.rs (1)

1-48: Well-documented crate with clean public API.

The crate documentation is comprehensive and provides a clear usage example. All imports in the example are valid: Repository is properly exported via soar_config::repository, and fetch_metadata and MetadataContent are correctly re-exported from the metadata module. The module structure and re-exports create an intentional, clean public API surface.

crates/soar-registry/src/package.rs (4)

1-16: LGTM!

The module documentation clearly describes the purpose and the FlexiBool enum is well-designed for handling flexible boolean deserialization from varied JSON formats.


18-24: LGTM!

The empty_is_none deserializer correctly normalizes empty strings to None, which is a common requirement for handling legacy or inconsistent JSON formats.


37-58: LGTM!

The flexible_bool deserializer comprehensively handles boolean values in multiple formats (actual booleans, yes/no, true/false, 1/0) with appropriate error handling for invalid inputs.


60-232: LGTM!

The RemotePackage struct is well-documented with comprehensive field coverage. The custom deserializers are applied consistently, and field aliases ensure backward compatibility with legacy metadata formats.

soar-cli/src/state.rs (4)

1-29: LGTM!

The imports are well-organized and correctly include all necessary items from the new soar_registry crate.


34-53: LGTM!

The handle_json_metadata helper function correctly handles JSON metadata by creating a fresh database, applying migrations, and importing the remote package data. The approach of removing the existing file before creating a new one ensures a clean state.


84-135: LGTM!

The sync_nests function correctly handles both metadata content variants. The use of a nest- prefix for repository names ensures nests are distinguishable from regular repositories in the database.


137-174: LGTM!

The init_repo_dbs function correctly handles both metadata content variants using the same pattern as sync_nests, ensuring consistency across the codebase.

crates/soar-registry/src/metadata.rs (7)

1-48: LGTM!

The module documentation is clear, and the MetadataContent enum provides a clean abstraction for handling both SQLite and JSON metadata formats.


50-62: LGTM!

The construct_nest_url function correctly handles the github: shorthand by expanding it to a full GitHub releases URL, with appropriate URL validation.


88-166: LGTM!

The fetch_nest_metadata function correctly implements conditional HTTP requests with ETag-based caching and respects the configured sync interval to minimize unnecessary network requests.


182-198: LGTM!

The fetch_public_key function correctly handles public key downloads with idempotent behavior (skipping if already exists).


243-321: LGTM!

The fetch_metadata function implements the same robust caching strategy as fetch_nest_metadata, with the additional capability to fetch public keys for signature verification.


435-444: LGTM!

The write_metadata_db function correctly uses buffered I/O for efficient writing and provides clear error context. The BufWriter automatically flushes on drop, ensuring data integrity.


324-339: [Your rewritten review comment text here]
[Exactly ONE classification tag]

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/soar-db/src/migration.rs (1)

38-49: Pre-existing bug: mark_first_pending always uses CORE_MIGRATIONS.

The mark_first_pending function hardcodes CORE_MIGRATIONS on Line 41, but it's called from apply_migrations which handles three different migration types (Core, Metadata, Nest). When applying Metadata or Nest migrations that encounter "already exists" errors, this function would incorrectly mark Core migrations as pending instead of the appropriate type.

Apply this diff to fix the function signature and usage:

 fn mark_first_pending(
     conn: &mut SqliteConnection,
+    source: EmbeddedMigrations,
 ) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
-    let pending = conn.pending_migrations(CORE_MIGRATIONS)?;
+    let pending = conn.pending_migrations(source)?;
     if let Some(first) = pending.first() {
         sql_query("INSERT INTO __diesel_schema_migrations (version) VALUES (?1)")
             .bind::<diesel::sql_types::Text, _>(first.name().version())
             .execute(conn)?;
     }

     Ok(())
 }

And update the call site:

         match conn.run_pending_migrations(source) {
             Ok(_) => break,
             Err(e) if e.to_string().contains("already exists") => {
-                mark_first_pending(conn)?;
+                mark_first_pending(conn, source)?;
             }
             Err(e) => return Err(e),
         }
🧹 Nitpick comments (2)
soar-cli/src/state.rs (2)

40-43: Non-atomic metadata replacement may leave inconsistent state on failure.

If migration or population fails after removing the existing file, the old metadata is lost without a successful replacement. Consider writing to a temporary file first, then atomically renaming on success.

This is acceptable for a cache that can be rebuilt, but worth noting for robustness.


112-115: Error conversion loses context.

Using .map_err(|e| SoarError::Custom(e.to_string())) discards the original error's chain and context. Consider implementing From<RegistryError> for SoarError or using a more context-preserving approach.

-                            write_metadata_db(&db_bytes, &metadata_db_path)
-                                .map_err(|e| SoarError::Custom(e.to_string()))?;
+                            write_metadata_db(&db_bytes, &metadata_db_path)
+                                .map_err(|e| SoarError::Custom(format!("writing nest metadata: {e:#}")))?;

The same pattern appears at line 157 for repository sync.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7e4279 and 89aaf05.

📒 Files selected for processing (6)
  • crates/soar-db/src/migration.rs (1 hunks)
  • crates/soar-registry/src/nest.rs (1 hunks)
  • soar-cli/src/nest.rs (1 hunks)
  • soar-cli/src/state.rs (9 hunks)
  • soar-core/src/database/connection.rs (1 hunks)
  • soar-core/src/database/repository.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • soar-cli/src/nest.rs
🧰 Additional context used
🧬 Code graph analysis (1)
soar-cli/src/state.rs (2)
crates/soar-registry/src/metadata.rs (3)
  • fetch_metadata (243-321)
  • fetch_nest_metadata (88-166)
  • write_metadata_db (435-444)
soar-core/src/utils.rs (1)
  • get_nests_db_conn (92-99)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: coverage
  • GitHub Check: test
🔇 Additional comments (5)
crates/soar-registry/src/nest.rs (1)

1-44: Well-structured data type with good documentation.

The Nest struct is clean, appropriately derived, and well-documented with clear explanations of URL formats. No issues found.

soar-cli/src/state.rs (2)

27-29: Imports align with new registry API.

The new imports from soar_registry properly expose the required functions and types for metadata handling.


150-164: Content handling follows consistent pattern.

The metadata content handling correctly distinguishes between SQLite database bytes and JSON packages, applying appropriate processing for each. The flow of writing content then validating packages is logically sound.

soar-core/src/database/connection.rs (1)

7-9: Import path updated correctly.

The import change mirrors the one in repository.rs and is consistent with the crate refactoring. The usage in from_remote_metadata is straightforward pass-through logic.

soar-core/src/database/repository.rs (1)

3-5: Import change verified—external RemotePackage type is fully compatible.

The transition from local to soar_registry::RemotePackage is correct. Verification confirms that the external type in soar-registry defines all fields accessed in insert_package (lines 59-161), including all optional and required fields with compatible types: disabled_reason, licenses, ghcr_files, homepages, notes, src_urls, tags, categories, snapshots, repology, replaces, recurse_provides, app_id, and all others. No type mismatches found.

@QaidVoid QaidVoid merged commit 21070db into main Dec 17, 2025
5 of 7 checks passed
github-actions bot pushed a commit to Azathothas/soar that referenced this pull request Dec 17, 2025
@QaidVoid QaidVoid mentioned this pull request Nov 23, 2025
@QaidVoid QaidVoid mentioned this pull request Dec 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants