Skip to content

Implement metadata fields#4

Merged
Athemis merged 62 commits intomainfrom
feature/elabftw-extra-fields
Dec 3, 2025
Merged

Implement metadata fields#4
Athemis merged 62 commits intomainfrom
feature/elabftw-extra-fields

Conversation

@Athemis
Copy link
Copy Markdown
Owner

@Athemis Athemis commented Dec 2, 2025

Summary

  • implements metadata fields compatible with eLabFTW extra fields syntax

Testing

  • cargo fmt
  • cargo clippy --all-targets --all-features
  • cargo test
  • Other:

Screenshots (if UI changes)

Notes for reviewers

Summary by CodeRabbit

  • New Features

    • Import, edit and manage eLabFTW-style extra metadata via a new UI section and file picker; per-field editor, grouping, validation, and save-time checks.
    • Saved archives now include per-field metadata entries, variableMeasured links, an embedded elabftw_metadata blob, and archive version.
  • Documentation

    • Added MVU layering guidance clarifying UI/state/IO separation.
  • Tests

    • Added tests covering import, validation, UI flows and archive metadata.

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

@Athemis Athemis self-assigned this Dec 2, 2025
@Athemis Athemis added the enhancement New feature or request label Dec 2, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2025

Walkthrough

A new eLabFTW extra‑fields feature: parsing/import models, UI editor (import/edit/grouping), MVU wiring (messages, commands, validation, save payload), and RO‑Crate export emitting eLabFTW‑style PropertyValue nodes plus an embedded elabftw_metadata blob; four dependencies added.

Changes

Cohort / File(s) Change Summary
Documentation & Guidelines
AGENTS.md
Added MVU layering guidance and documented enhanced export format emitting eLabFTW‑style PropertyValue nodes linked via variableMeasured plus an elabftw_metadata blob.
Dependencies
Cargo.toml
Added dependencies: serde = { version = "1.0", features = ["derive"] }, url = { version = "2", default-features = false, features = ["std"] }, uuid = { version = "1", features = ["v4"] }, email_address = "0.2".
Data Models
src/models/extra_fields.rs, src/models/mod.rs
New extra_fields module: types ExtraFieldKind, ExtraField, ExtraFieldGroup, ExtraFieldsImport; parse_elabftw_extra_fields() and validate_field() added; implements deserialization, normalization, grouping, sorting and tests.
Export Logic & RO‑Crate Integration
src/logic/eln.rs
Extended build_and_write_archive() signature to accept extra_fields/extra_groups; added ExtraFieldsExport, build_extra_fields_export(), reconstruct_elabftw_metadata(), value_to_json(); integrates PropertyValue nodes, variableMeasured links and root versioning; tests updated.
MVU Layer Integration
src/mvu/mod.rs
AppModel gains extra_fields: ExtraFieldsModel; added Msg::ExtraFields, Command::PickExtraFieldsFile; update() delegates to extra_fields; run_command() handles JSON file pick/parse; SavePayload includes extra_fields/extra_groups; validation extended for fields.
UI Component & Presentation
src/ui/components/extra_fields.rs, src/ui/components/mod.rs, src/ui/mod.rs
New extra_fields UI component with ExtraFieldsModel, messages/commands/events, update() and view(); supports import, edit/create/delete, modal editor, validation and duplicate-name checks; integrated via render_extra_fields_section(); tests added.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant UI as "UI Layer"
participant MVU as "MVU State"
participant Parser as "ExtraFields Parser"
participant Validator as "Field Validator"
participant Export as "ELN Export (RO‑Crate)"

User->>UI: Click "Import Metadata"
UI->>MVU: Msg::ExtraFields(PickExtraFieldsFile)
MVU->>UI: Command::PickExtraFieldsFile
User->>UI: Select JSON file
UI->>Parser: parse_elabftw_extra_fields(json)
Parser-->>MVU: ExtraFieldsImport (fields & groups)
MVU->>MVU: Store fields & groups
User->>UI: Edit fields, Save
UI->>MVU: Msg::SavePayload (includes extra_fields/groups)
MVU->>Validator: validate_field() for each field
Validator-->>MVU: Validation results
MVU->>Export: build_and_write_archive(..., extra_fields, extra_groups)
Export->>Export: build_extra_fields_export() & reconstruct_elabftw_metadata()
Export-->>User: RO‑Crate archive with PropertyValue nodes & metadata blob

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–75 minutes

  • Areas needing focused review:
    • src/models/extra_fields.rs — deserialization edge cases, multi-value normalization, kind mapping and validation.
    • src/ui/components/extra_fields.rs — UI state transitions, modal lifecycle, group reassignment, duplicate-name handling.
    • src/logic/eln.rs — RO‑Crate graph construction, PropertyValue node shapes, variableMeasured linkage, and embedded elabftw_metadata.
    • src/mvu/mod.rs — file-pick command wiring, async handling, and save payload integration.

Poem

Engage: a JSON tide arrives at dawn,
Fields fall into ranks — each label drawn,
PropertyValues hum within the crate,
A tiny blob remembers what we state,
🌌 Make it so.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Implement metadata fields' accurately summarizes the main change: adding eLabFTW-compatible extra fields metadata support throughout the codebase.
Description check ✅ Passed The description follows the template structure with a summary and completed testing checklist, though notes for reviewers are absent.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/elabftw-extra-fields

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e55f8a and 42f8cc5.

📒 Files selected for processing (2)
  • src/logic/eln.rs (11 hunks)
  • src/ui/components/extra_fields.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
src/logic/eln.rs

📄 CodeRabbit inference engine (AGENTS.md)

In src/logic/eln.rs: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Files:

  • src/logic/eln.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/extra_fields.rs
🧠 Learnings (10)
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Align archive structure and metadata with the ELN File Format specification: https://raw.githubusercontent.com/TheELNConsortium/TheELNFileFormat/refs/heads/master/SPECIFICATION.md

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Test business logic in `src/logic/eln.rs` independently of UI; mock file system operations where appropriate

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Use `cargo test` for unit and integration coverage; colocate simple unit tests with modules and broader scenarios under `tests/`

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/components/attachments.rs : In `src/ui/components/attachments.rs`: compute `sanitized_name` using `sanitize_component`, show WARNING icon on sanitized mismatch, emit commands for file picking/hashing/thumbnails, and ensure edited names are sanitized/deduped

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run `cargo fmt` before committing

Applied to files:

  • src/ui/components/extra_fields.rs
🧬 Code graph analysis (1)
src/ui/components/extra_fields.rs (1)
src/models/extra_fields.rs (5)
  • validate_field (155-205)
  • value (177-177)
  • value (187-187)
  • s (335-335)
  • s (366-366)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (17)
src/ui/components/extra_fields.rs (11)

10-71: Make it so, Ensign – the struct definitions are sound.

The model and draft structures are well-organized. The FieldDraft default implementation includes thorough documentation. The private nature of FieldDraft appropriately encapsulates implementation details from external consumers.


73-210: The accessor methods are logical and well-constructed.

The encapsulation is commendable – returning slices for fields() and groups() prevents uncontrolled mutation. The fallback logic in display_group_name and the tie-breaking by (position, id) in lowest_position_group_id demonstrate attention to edge cases. Engage.


212-285: The message architecture follows sound MVU protocols.

The ExtraFieldsMsg enum provides a comprehensive vocabulary for user interactions. The separation of ExtraFieldsCommand for side effects and ExtraFieldsEvent for feedback aligns with the project's MVU architecture. This is precisely the kind of disciplined design I expect from this crew.


287-600: The update function handles state transitions with precision.

I am particularly impressed by the handling of edge cases: the RemoveGroup logic that renames the final group to "Default" rather than leaving the model in an inconsistent state, and the CommitFieldModal path that prevents duplicate field names while keeping the modal open for correction. This demonstrates the foresight one expects when charting a course through complex state space. Engage.


602-754: The view composition is exemplary.

The rendering logic follows a clear hierarchy: collapsible metadata section, action buttons, grouped field lists, and the modal overlay. The use of Phosphor icons via egui_phosphor::regular::* adheres to project conventions. The empty state messaging provides appropriate guidance to users. Well done, Number One.


756-835: The group header controls demonstrate sound user interface discipline.

The conditional rendering – showing edit controls when editing, action buttons otherwise – provides a clear modal editing experience. Preventing removal of the final group maintains data integrity. The implementation is entirely satisfactory.


837-950: The field rendering exhibits attention to visual feedback.

The invalid field highlighting – blending with the theme's background color to create an adaptive tint – is a sophisticated approach that will function across light and dark themes. The dispatch to specialized renderers by field kind maintains separation of concerns. This is the kind of quality engineering I require.


952-1130: The readonly contract is now properly honored across all field types.

Each renderer correctly gates interactive widgets behind the readonly check: ui.add_enabled_ui(!field.readonly, ...) for grouped controls and ui.add_enabled(!disabled, ...) for individual widgets. The prior concerns have been addressed with precision. Well executed.


1132-1359: The helper functions provide essential support infrastructure.

The name_conflict function's case-insensitive comparison and self-exclusion logic prevents false conflicts during edits. The apply_draft_to_field function correctly scopes option and unit application to the appropriate field kinds. These utilities are both efficient and correct.


1361-1542: The modal implementation follows sound egui window patterns.

The edit modal correctly clones the draft for rendering immutability, validates before enabling save, and restricts kind changes to creation only – preventing destabilizing modifications to existing fields. The fixed-width conflict message with transparent color when valid maintains layout stability. This is disciplined UI engineering.


1544-2057: The test suite provides commendable coverage of critical paths.

The tests address import loading, validation states, field CRUD operations, modal save/cancel behavior, duplicate detection, and group reassignment on removal. The test naming follows behavioral conventions. I am satisfied with this coverage, Number One.

src/logic/eln.rs (6)

25-36: The version constant and export structure are properly documented.

The ELN_FORMAT_VERSION constant with its explanatory comment addresses the prior concern about the hardcoded value. The ExtraFieldsExport struct appropriately encapsulates the export components. This is the documentation clarity I expect from a Starfleet crew. Make it so.


89-302: The archive builder integrates extra fields with precision.

The function signature expansion is justified by the explicit parameter requirements documented in the clippy allow. The integration pattern – building the export, linking variableMeasured by ID, and extending the graph – follows RO-Crate conventions. The version field now uses the named constant. This is a well-executed enhancement to the archive infrastructure.


304-384: The PropertyValue generation follows eLabFTW protocols.

The structure of each PropertyValue node – with propertyID as the field label, valueReference as the kind, and optional unitText/description – mirrors eLabFTW's export format. The metadata blob insertion at position zero ensures round-trip compatibility. The use of random UUIDs for unique identifiers is acceptable, though it does mean archives are not byte-reproducible across runs.

Ensign, confirm that the non-reproducibility of archive bytes (due to random UUIDs) is acceptable for your use case. If deterministic archives are needed for verification or caching purposes, you may need to derive IDs from content hashes instead.


386-517: The metadata reconstruction faithfully replicates eLabFTW's structure.

The hierarchical organization – elabftw settings with extra_fields_groups, and extra_fields keyed by label – matches eLabFTW's expectations. All field properties are correctly serialized, including conditionally-present fields like readonly and blank_value_on_duplicate. This enables proper round-trip compatibility. Excellent work.


519-581: The value serialization logic handles type variations correctly.

The decision to export Number fields as strings for eLabFTW compatibility is well-documented. The integer parsing with string fallback for Items/Experiments/Users kinds gracefully handles both valid IDs and edge cases. The multi-value handling produces the expected array structure. This demonstrates thorough understanding of the target format.


683-784: The test verifies extra fields integration with admirable thoroughness.

The test constructs an archive with a representative Select field, then validates the RO-Crate metadata structure: the experiment's variableMeasured linkage, the PropertyValue node properties, and the embedded elabftw_metadata blob content. This provides confidence that the export format aligns with eLabFTW's expectations. The testing approach is exactly what I would expect from this engineering team.


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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #7

coderabbitai bot added a commit that referenced this pull request Dec 2, 2025
Docstrings generation was requested by @Athemis.

* #4 (comment)

The following files were modified:

* `src/logic/eln.rs`
* `src/models/extra_fields.rs`
* `src/mvu/mod.rs`
* `src/ui/components/extra_fields.rs`
* `src/ui/mod.rs`
Copy link
Copy Markdown
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

🧹 Nitpick comments (5)
src/ui/components/extra_fields.rs (3)

77-86: A minor observation, Ensign.

The unwrap_or_else fallback on line 85 will never execute since ensure_default_group() guarantees at least one group exists when groups.is_empty() on line 78. The defensive fallback is harmless but logically unreachable.

     pub fn lowest_position_group_id(&mut self) -> i32 {
         if self.groups.is_empty() {
             return self.ensure_default_group();
         }
         self.groups
             .iter()
             .min_by_key(|g| (g.position, g.id))
             .map(|g| g.id)
-            .unwrap_or_else(|| self.ensure_default_group())
+            .expect("groups guaranteed non-empty")
     }

449-475: The logic is sound, yet I detect redundancy in our approach.

When removing the last group (lines 455-459), reassigning f.group_id to Some(g.id) is redundant—the field already has group_id == Some(group.id), and g.id == group.id. The assignment changes nothing. Consider removing this loop or documenting its purpose if it serves future needs.

                 if removing_last {
                     if let Some(g) = model.groups.get_mut(idx) {
                         g.name = "Default".into();
-                        for f in model.fields.iter_mut() {
-                            if f.group_id == Some(group.id) {
-                                f.group_id = Some(g.id);
-                            }
-                        }
+                        // Field group_id remains unchanged since group id is preserved
                     }
                 } else {

896-904: A minor inefficiency, but the mission proceeds.

Line 897 lowercases the key, then line 902 uses eq_ignore_ascii_case. The lowercase conversion is redundant since the case-insensitive comparison handles it. Consider simplifying:

 fn name_conflict(model: &ExtraFieldsModel, label: &str, editing: Option<usize>) -> bool {
     let key = label.trim().to_lowercase();
     if key.is_empty() {
         return false;
     }
     model.fields.iter().enumerate().any(|(idx, f)| {
-        idx != editing.unwrap_or(usize::MAX) && f.label.trim().eq_ignore_ascii_case(&key)
+        idx != editing.unwrap_or(usize::MAX) && f.label.trim().to_lowercase() == key
     })
 }
src/models/extra_fields.rs (2)

267-274: A note on the catch-all case, Commander.

Line 272's fallback uses to_string() on the remaining Value variants (arrays, objects, null). For null, this yields "null" as a string. For nested objects/arrays, it produces JSON text. This behavior may be intentional for edge cases, but consider whether Value::Null should return None instead.

 fn value_to_string(val: Option<&Value>) -> Option<String> {
     match val? {
         Value::String(s) => Some(s.clone()),
         Value::Number(n) => Some(n.to_string()),
         Value::Bool(b) => Some(if *b { "on" } else { "" }.to_string()),
+        Value::Null => None,
         other => Some(other.to_string()),
     }
 }

276-299: The test provides a solid foundation, though the voyage could extend further.

The parsing test verifies core functionality with a representative payload. Consider adding tests for edge cases when time permits:

  • Missing elabftw block (groups absent)
  • Unknown field types
  • Empty extra_fields
  • Malformed JSON error handling

For a draft PR, the current coverage is sufficient to proceed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c011386 and c539b9f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • AGENTS.md (1 hunks)
  • Cargo.toml (2 hunks)
  • src/logic/eln.rs (3 hunks)
  • src/models/extra_fields.rs (1 hunks)
  • src/models/mod.rs (1 hunks)
  • src/mvu/mod.rs (12 hunks)
  • src/ui/components/extra_fields.rs (1 hunks)
  • src/ui/components/mod.rs (1 hunks)
  • src/ui/mod.rs (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
src/mvu/mod.rs (2)
src/ui/components/extra_fields.rs (2)
  • update (171-477)
  • default (36-48)
src/models/extra_fields.rs (4)
  • parse_elabftw_extra_fields (185-265)
  • validate_field (81-121)
  • value (103-103)
  • value (113-113)
src/ui/components/extra_fields.rs (1)
src/models/extra_fields.rs (5)
  • validate_field (81-121)
  • value (103-103)
  • value (113-113)
  • s (223-223)
  • s (254-254)
src/ui/mod.rs (1)
src/ui/components/extra_fields.rs (1)
  • view (480-533)
src/models/extra_fields.rs (1)
src/ui/components/extra_fields.rs (2)
  • fields (52-54)
  • groups (56-58)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (27)
Cargo.toml (1)

16-16: Make it so — these dependencies are well-chosen for the mission, Number One.

The addition of serde with the derive feature and the url crate with minimal features provides precisely what is required for JSON deserialization and URL validation in the extra fields implementation. A most logical and efficient selection.

Also applies to: 29-29

AGENTS.md (1)

36-42: Excellent documentation of our operational protocols, Ensign.

This layering enforcement guidance establishes clear boundaries for the MVU architecture — UI components remain pure, validation logic resides in appropriate modules, and IO is properly isolated to run_command. These directives will serve the crew well in maintaining architectural discipline.

src/models/mod.rs (1)

7-7: The domain layer is properly extended, Lieutenant.

The new extra_fields module is correctly exposed alongside the existing attachment and keywords modules, maintaining consistent organization within the domain layer.

src/ui/components/mod.rs (1)

8-8: The bridge accepts your new station, Ensign.

The extra_fields UI component module is properly declared alongside the existing components. The alphabetical ordering is maintained — an orderly ship is an efficient ship.

src/ui/mod.rs (4)

13-13: Import acknowledged and logged, Ensign.

The extra_fields module is properly brought into scope following the established pattern for other UI components.


130-132: The extra fields section is properly integrated into our main viewer.

The placement between keywords and attachments follows a logical progression, and the spacing maintains visual consistency with other sections. Standard procedure, executed well.


153-154: A prudent validation gate for the save operation.

The save button now requires both a non-empty title and valid extra fields before engaging. The updated hover text provides clear guidance to the user regarding why the operation is disabled. This defensive approach prevents incomplete data from being archived.

Also applies to: 162-162


269-272: The new section renderer follows established protocols precisely.

The render_extra_fields_section method correctly delegates to the component's view function and maps the resulting messages through the Msg::ExtraFields wrapper. This maintains the MVU message flow architecture we have established across the ship.

src/logic/eln.rs (2)

86-93: Ensign, I observe we have accepted new cargo but have not yet unpacked it.

The extra_fields and extra_groups parameters are added to the function signature but immediately suppressed with let _ = (extra_fields, extra_groups);. This appears to be a placeholder for future implementation where the extra fields metadata will be written into the RO-Crate archive.

Is this intentional for this PR, with the actual archive integration planned for a follow-up? If so, consider adding a TODO comment to clarify the intended next steps:

+    // TODO: Serialize extra_fields and extra_groups into experiment_node metadata
     let _ = (extra_fields, extra_groups);

376-377: Test parameters updated accordingly — well done.

The test correctly passes empty slices for the new extra_fields and extra_groups parameters.

src/mvu/mod.rs (6)

37-38: The AppModel now carries the extra fields state — proper integration with ship systems.

The extra_fields field is correctly added to AppModel with appropriate documentation, following the established pattern for other component models.


69-69: Message and Command variants properly declared for the new subsystem.

The ExtraFields message variant and PickExtraFieldsFile command variant extend the MVU kernel appropriately to handle the extra fields workflow.

Also applies to: 78-78


172-182: The message routing follows established patterns — exemplary, Lieutenant.

The Msg::ExtraFields handler correctly delegates to extra_fields::update, surfaces events, and translates component commands to top-level commands. This mirrors the handling of Attachments and Keywords messages precisely.


206-232: The file import command is well-structured, Number One.

The PickExtraFieldsFile handler:

  1. Opens a filtered file dialog for JSON files
  2. Reads the file content
  3. Parses with parse_elabftw_extra_fields
  4. Returns appropriate messages for success, parse failure, read failure, and cancellation

Error handling is comprehensive and user-facing messages are clear. A solid implementation.


300-311: Field validation before saving — a wise precaution.

The validation loop correctly checks each extra field and returns user-friendly error messages. The fail-fast approach (returning on first error) provides immediate feedback.

One consideration: if a user has multiple invalid fields, they will only see one error at a time. This is acceptable for now, but you may wish to collect all validation errors in a future enhancement to improve the user experience.


445-600: Comprehensive test coverage for the validation logic — commendable work, crew.

The tests thoroughly exercise:

  • Invalid and valid URL fields
  • Invalid and valid number fields
  • Invalid and valid integer fields

The helper functions add_url_field and add_typed_field demonstrate proper MVU message flow for setting up test state. This test suite will serve us well in detecting regressions.

src/ui/components/extra_fields.rs (8)

10-20: Make it so! The model structure is well-organized.

Number One, the ExtraFieldsModel struct follows sound principles with clear separation of concerns. The state for editing groups and fields is appropriately encapsulated.


22-49: Engage. The draft structure serves its purpose admirably.

The FieldDraft helper struct cleanly isolates temporary edit state from the persistent model—a logical approach for modal-based editing.


96-155: The command structure is exemplary, Number One.

This message enum demonstrates the discipline of a well-trained crew—each variant clearly corresponds to a user interaction or system event. The MVU pattern is faithfully implemented.


645-666: The visual feedback system is adequate, Lieutenant.

The frame styling with conditional stroke and fill for invalid fields provides clear visual feedback. The color blending arithmetic (lines 660-662) safely promotes to u16 before averaging—no overflow risk. Well executed.


736-773: Steady as she goes.

The options rendering handles both multi-select (checkboxes) and single-select (radio) modes appropriately. The chosen values are correctly synchronized with the model state.


939-1097: The modal implementation demonstrates commendable attention to user experience.

The field editor modal properly validates input, shows conflict feedback without layout jumps (lines 971-982), and conditionally renders kind selection only during creation. The egui patterns are correctly applied.


1099-1130: Excellent test coverage, Commander.

The test suite demonstrates thorough verification of the update logic. The helper functions make_field and make_group reduce boilerplate effectively. Key scenarios including imports, validation, group management, and name conflict detection are all addressed.


279-290: The project uses edition = "2024", which fully supports let-chain syntax. The let chains used at lines 279-290 and elsewhere are compatible with the declared Rust edition and do not require verification or any changes.

Likely an incorrect or invalid review comment.

src/models/extra_fields.rs (3)

14-52: The type system serves us well here, Mr. La Forge.

The ExtraFieldKind enum with its Unknown(String) variant provides forward compatibility for unrecognized field types from eLabFTW. The from_str implementation is straightforward and exhaustive.


184-265: The parsing logic is thorough and handles edge cases admirably.

The function correctly handles:

  • Multi-value arrays (lines 204-219)
  • Flexible group_id parsing from Number or String (lines 221-225)
  • Deterministic sorting by position and label (line 245)
  • Group position derived from enumeration order (line 251)

This is precisely the kind of defensive parsing required when interfacing with external systems.


99-108: Number field accepts special float values like "inf", "NaN", and scientific notation.

The f64::parse call on line 103 accepts strings like "inf", "-inf", "NaN", and scientific notation (e.g., "1e10"). eLabFTW's documentation does not explicitly define whether these values should be accepted for the "number" field type. If the application should restrict input to finite decimal numbers, add a check like value.parse::<f64>().map(|n| n.is_finite()).unwrap_or(false) on line 103. Otherwise, document this behavior as intentional.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2025

UTG Post-Process Complete

No new issues were detected in the generated code and all check runs have completed. The unit test generation process has completed successfully.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2025

Creating a PR to put the unit tests in...

The changes have been created in this pull request: View PR

@Athemis Athemis marked this pull request as ready for review December 2, 2025 20:23
Copy link
Copy Markdown
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: 2

🧹 Nitpick comments (5)
src/models/extra_fields.rs (2)

33-51: Make it so — but consider the established protocols.

The from_str method functions correctly; however, implementing std::str::FromStr trait would align with Rust's standard conventions and enable use of .parse() syntax. This is not urgent — the current implementation serves its purpose admirably.

+impl std::str::FromStr for ExtraFieldKind {
+    type Err = std::convert::Infallible;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
+            "text" => Self::Text,
+            "number" => Self::Number,
+            "select" => Self::Select,
+            "checkbox" => Self::Checkbox,
+            "date" => Self::Date,
+            "datetime-local" => Self::DateTimeLocal,
+            "time" => Self::Time,
+            "url" => Self::Url,
+            "email" => Self::Email,
+            "radio" => Self::Radio,
+            "items" => Self::Items,
+            "experiments" => Self::Experiments,
+            "users" => Self::Users,
+            other => Self::Unknown(other.to_string()),
+        })
+    }
+}

300-319: The test validates the primary use case, but we must consider edge scenarios.

This test confirms parsing of a representative payload. Consider adding tests for edge cases: empty extra_fields object, missing elabftw block, fields with multi-value arrays, and malformed JSON to ensure robust error handling.

src/logic/eln.rs (3)

91-103: The function signature grows complex — a builder pattern may be warranted in future.

With ten parameters, this function approaches the limits of ergonomic API design. The #[allow(clippy::too_many_arguments)] annotation acknowledges this debt. When circumstances permit, consider consolidating parameters into a configuration struct or builder pattern.


436-442: A hardcoded value warrants consideration.

The "display_main_text": true is hardcoded at line 438. If this property can vary in imported metadata, consider preserving the original value during parsing rather than always setting it to true on export.


448-484: The _shape parameter is unused — is this intentional, Counselor?

The ValueShape enum and the _shape parameter in value_to_json are defined but the parameter is never utilized in the function body. If different shaping behavior is planned for Property vs Metadata contexts, implement it; otherwise, remove the unused infrastructure.

-enum ValueShape {
-    /// Shape used in PropertyValue nodes.
-    Property,
-    /// Shape used in the embedded eLabFTW metadata JSON.
-    Metadata,
-}
-
-fn value_to_json(field: &ExtraField, _shape: ValueShape) -> serde_json::Value {
+fn value_to_json(field: &ExtraField) -> serde_json::Value {

And update call sites accordingly.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c539b9f and 5e14144.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • AGENTS.md (2 hunks)
  • Cargo.toml (2 hunks)
  • src/logic/eln.rs (11 hunks)
  • src/models/extra_fields.rs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • AGENTS.md
🧰 Additional context used
🧬 Code graph analysis (2)
src/logic/eln.rs (3)
src/utils/sanitize_component.rs (1)
  • sanitize_component (17-110)
src/ui/components/extra_fields.rs (2)
  • fields (52-54)
  • groups (56-58)
src/models/extra_fields.rs (6)
  • value (123-123)
  • value (133-133)
  • from_str (34-51)
  • as_str (54-71)
  • raw (213-217)
  • raw (218-222)
src/models/extra_fields.rs (1)
src/ui/components/extra_fields.rs (2)
  • fields (52-54)
  • groups (56-58)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (7)
Cargo.toml (1)

16-16: The new dependencies are well-suited for this mission, Number One.

The addition of serde, url, and uuid crates aligns precisely with the requirements for parsing eLabFTW metadata JSON, validating URLs, and generating unique identifiers for RO-Crate graph nodes. These are battle-tested libraries — a sound strategic choice.

Also applies to: 29-30

src/models/extra_fields.rs (3)

74-98: The structural design here is commendable, Ensign.

The ExtraField struct captures the full complexity of eLabFTW field definitions. The cmp_key method's use of i32::MAX as a fallback ensures deterministic ordering — fields without explicit positions will sort to the end, maintaining a predictable display order.


204-285: Excellent work on the parsing logic — systematic and thorough.

The implementation handles the varied shapes of eLabFTW JSON data with appropriate flexibility: arrays vs. single values, numeric vs. string IDs. Using BTreeMap provides deterministic iteration order before the explicit sort by position. The error context propagation will aid in diagnosing malformed input.


287-294: The value conversion logic is sound.

Converting booleans to "on"/"" aligns with HTML checkbox conventions — a sensible choice for eLabFTW compatibility. The fallback to other.to_string() ensures unexpected JSON types (objects, arrays, null) are handled gracefully.

src/logic/eln.rs (3)

25-33: A well-structured internal representation, Lieutenant.

The ExtraFieldsExport struct cleanly encapsulates the components needed for RO-Crate graph construction — the individual PropertyValue nodes, the metadata blob, and the ID references for linking. This separation of concerns will serve us well.


586-674: An exemplary integration test — thorough and well-structured.

This test validates the complete export pipeline: archive creation, RO-Crate graph structure, PropertyValue nodes, and the embedded metadata blob. The use of tempfile ensures test isolation. Engage.


270-330: The export logic is well-structured and follows the project's documented conventions.

UUID v4 ensures unique identifiers for each PropertyValue node. The pv:// URI scheme is an intentional project design choice documented in AGENTS.md for eLabFTW-style exports and works within the JSON-LD framework when paired with proper @context definitions.

@Athemis Athemis force-pushed the feature/elabftw-extra-fields branch from 1c8feda to 7332d30 Compare December 2, 2025 20:49
Copy link
Copy Markdown
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

♻️ Duplicate comments (2)
src/logic/eln.rs (1)

25-26: Excellent, Number One — the constant extraction is precisely what was required.

The ELN_FORMAT_VERSION constant with documentation addresses the prior review feedback. The comment clarifies that eLabFTW expects version 103+ for id-based variableMeasured, which provides essential context for future maintainers.

src/models/extra_fields.rs (1)

140-149: The email validation has been properly implemented, addressing prior feedback.

Using the email_address crate for validation is a sound choice — it handles the complexity of RFC-compliant email parsing that would be error-prone to implement manually.

🧹 Nitpick comments (3)
src/logic/eln.rs (2)

439-445: Consider whether display_main_text should be configurable.

The "display_main_text": true is hardcoded in the reconstructed metadata. If this setting was present in the original import, it may have had a different value. Consider preserving the original value or making this configurable if round-trip fidelity is important for all eLabFTW metadata properties.


451-487: The ValueShape parameter stands at attention but sees no action.

The _shape parameter in value_to_json is currently unused — both Property and Metadata variants produce identical serialization. If this is scaffolding for future differentiation (e.g., numbers as strings vs. actual JSON numbers), consider adding a TODO comment. Otherwise, if the distinction is no longer needed, the enum and parameter could be removed to reduce cognitive overhead.

-/// Convert a field's value into the most appropriate JSON type.
-enum ValueShape {
-    /// Shape used in PropertyValue nodes.
-    Property,
-    /// Shape used in the embedded eLabFTW metadata JSON.
-    Metadata,
-}
-
-fn value_to_json(field: &ExtraField, _shape: ValueShape) -> serde_json::Value {
+/// Convert a field's value into the most appropriate JSON type.
+fn value_to_json(field: &ExtraField) -> serde_json::Value {

Update call sites accordingly if the distinction is not needed.

src/models/extra_fields.rs (1)

109-151: Date and time fields proceed unchallenged through our defenses.

The Date, Time, and DateTimeLocal variants fall through to the default branch without format validation. If eLabFTW expects specific ISO 8601 formats, consider adding validation. However, if the formats are flexible or validation occurs elsewhere, this may be acceptable.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ba09faa and 988ad38.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml (2 hunks)
  • src/logic/eln.rs (11 hunks)
  • src/models/extra_fields.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/logic/eln.rs
  • src/models/extra_fields.rs
src/logic/eln.rs

📄 CodeRabbit inference engine (AGENTS.md)

In src/logic/eln.rs: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Files:

  • src/logic/eln.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/logic/eln.rs
  • src/models/extra_fields.rs
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Align archive structure and metadata with the ELN File Format specification: https://raw.githubusercontent.com/TheELNConsortium/TheELNFileFormat/refs/heads/master/SPECIFICATION.md

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Use `cargo test` for unit and integration coverage; colocate simple unit tests with modules and broader scenarios under `tests/`

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Test business logic in `src/logic/eln.rs` independently of UI; mock file system operations where appropriate

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/components/attachments.rs : In `src/ui/components/attachments.rs`: compute `sanitized_name` using `sanitize_component`, show WARNING icon on sanitized mismatch, emit commands for file picking/hashing/thumbnails, and ensure edited names are sanitized/deduped

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/**/*.rs : Use `rfd` crate for cross-platform native file dialog operations

Applied to files:

  • Cargo.toml
🧬 Code graph analysis (2)
src/logic/eln.rs (5)
src/models/attachment.rs (2)
  • assert_unique_sanitized_names (46-57)
  • new (28-42)
src/utils/hash.rs (1)
  • hash_file (17-24)
src/utils/sanitize_component.rs (1)
  • sanitize_component (17-110)
src/ui/components/extra_fields.rs (2)
  • fields (52-54)
  • groups (56-58)
src/models/extra_fields.rs (3)
  • value (124-124)
  • value (134-134)
  • as_str (55-72)
src/models/extra_fields.rs (2)
src/logic/eln.rs (1)
  • field (479-481)
src/ui/components/extra_fields.rs (3)
  • default (36-48)
  • fields (52-54)
  • groups (56-58)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (9)
Cargo.toml (1)

16-16: Make it so — these dependency additions are logical and well-structured.

The new dependencies align precisely with the extra fields functionality: serde for serialization, url for URL validation, uuid for generating PropertyValue identifiers, and email_address for email validation. The version specifications follow the established pattern in this manifest.

Also applies to: 29-31

src/logic/eln.rs (3)

28-36: A well-organized structure, befitting Starfleet engineering standards.

The ExtraFieldsExport struct cleanly bundles the export artifacts. The documentation on each field clarifies their purpose in the RO-Crate graph construction.


273-333: The implementation follows eLabFTW conventions admirably.

The UUID-based pv:// identifiers and the structure of PropertyValue nodes align with eLabFTW export patterns. The metadata blob is correctly positioned first in variable_measured_ids (Line 319) to ensure round-trip compatibility.


589-677: Ensign, this test provides excellent coverage of the new functionality.

The test validates the complete extra fields export flow: PropertyValue node creation, metadata blob generation, and proper linkage via variableMeasured. The assertions verify both structural correctness and content fidelity, which aligns with the coding guidelines for testing business logic independently.

src/models/extra_fields.rs (5)

1-5: The SPDX headers and module documentation are in proper order, Number One.

The file correctly includes the SPDX license identifier and copyright text as required by the coding guidelines. The module-level documentation with //! provides appropriate context for the module's purpose.


15-73: A logical enumeration that handles the unknown with grace.

The Unknown(String) variant ensures forward compatibility with field types we may not yet recognize. The from_str/as_str pair maintains round-trip consistency — precisely the kind of defensive engineering I expect.


215-296: The parsing logic navigates the JSON structure with precision.

Using BTreeMap ensures deterministic field ordering, and the polymorphic value handling (String, Number, Bool, Array) correctly adapts to eLabFTW's flexible JSON schema. The sorting by position then label provides consistent ordering for the UI layer.


298-305: A pragmatic conversion function, Lieutenant.

The boolean-to-string mapping ("on" for true, "" for false) aligns with HTML checkbox conventions. The fallback to to_string() for other types is reasonable for an internal helper where complex nested values are unlikely.


307-330: The test covers the primary parsing path effectively.

The test validates group and field parsing with realistic data including Unicode characters. Consider adding tests for edge cases such as missing fields, empty extra_fields objects, or malformed JSON to ensure robust error handling — though this could be deferred to a follow-up.

Copy link
Copy Markdown
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

🧹 Nitpick comments (4)
src/mvu/mod.rs (1)

300-311: Validation with diplomatic error messages.

The field validation loop translates terse error codes into user-friendly messages. The early return on first error is a reasonable UX choice—addressing one issue at a time prevents overwhelming the user.

One observation, Commander: the match arms use string literals ("required", "invalid_url", etc.) for error discrimination. While functional, an enum-based approach would provide compile-time guarantees. However, this is a minor consideration for a later mission.

Consider using an enum for validation errors rather than string codes to leverage the type system:

// In models/extra_fields.rs
pub enum FieldValidationError {
    Required,
    InvalidUrl,
    InvalidNumber,
    InvalidInteger,
}

This would make the match exhaustive and catch any new error variants at compile time.

src/logic/eln.rs (3)

89-106: Extend the archive function docs to mention extra field metadata.

The public build_and_write_archive now threads extra_fields and extra_groups into the RO‑Crate, yet the Rustdoc still only describes attachments and core metadata. I recommend updating the summary and parameters to note that eLabFTW‑style extra fields are exported as PropertyValue nodes plus an embedded elabftw_metadata blob, so future readers understand the full contract. Make it clear, make it precise.


273-333: Clarify or simplify the ValueShape abstraction when time permits.

At present, ValueShape is threaded into value_to_json, but the function ignores the shape and always applies the same conversion rules. From the captain’s chair, this looks like an unfinished abstraction: either we intend different representations for PropertyValue vs embedded metadata, or we can drop the enum parameter entirely for now.

I suggest either:

  • Implementing divergent behaviour per shape (if required by eLabFTW/RO‑Crate semantics), or
  • Removing ValueShape and its argument until such divergence is needed, to keep the code as simple as the mission allows.

This is a tidy‑up, not a blocker.

Also applies to: 451-487


283-319: Consider deterministic IDs for PropertyValue nodes for reproducible archives.

Using Uuid::new_v4() for each extra field and the metadata blob ensures uniqueness, but it also means two exports of identical input will differ at the byte level. If reproducible ELN archives are a goal, it may be worth deriving stable IDs (for example, from field labels or a hash) so that variableMeasured and PropertyValue @ids are deterministic.

This is strategically optional, but worth contemplating before long‑term adoption of these IDs.

If reproducibility matters, please confirm current requirements and decide whether to keep random UUIDs or switch to a deterministic scheme before other systems start depending on these identifiers.

Also applies to: 200-221, 253-261, 589-677

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 686fc31 and 988ad38.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • AGENTS.md (2 hunks)
  • Cargo.toml (2 hunks)
  • src/logic/eln.rs (11 hunks)
  • src/models/extra_fields.rs (1 hunks)
  • src/models/mod.rs (1 hunks)
  • src/mvu/mod.rs (12 hunks)
  • src/ui/components/extra_fields.rs (1 hunks)
  • src/ui/components/mod.rs (1 hunks)
  • src/ui/mod.rs (4 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/ui/components/mod.rs
  • src/ui/mod.rs
  • src/models/mod.rs
  • src/ui/components/extra_fields.rs
  • src/logic/eln.rs
  • src/mvu/mod.rs
  • src/models/extra_fields.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/mod.rs
  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/mod.rs
  • src/ui/mod.rs
  • src/ui/components/extra_fields.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/ui/components/mod.rs
  • src/ui/mod.rs
  • src/models/mod.rs
  • src/ui/components/extra_fields.rs
  • src/logic/eln.rs
  • src/mvu/mod.rs
  • src/models/extra_fields.rs
src/logic/eln.rs

📄 CodeRabbit inference engine (AGENTS.md)

In src/logic/eln.rs: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Files:

  • src/logic/eln.rs
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/ui/mod.rs
  • src/ui/components/extra_fields.rs
  • src/mvu/mod.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/mod.rs
  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/ui/mod.rs
  • src/ui/components/extra_fields.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Applied to files:

  • src/ui/mod.rs
  • src/models/mod.rs
  • src/logic/eln.rs
  • src/mvu/mod.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/mod.rs
  • src/ui/components/extra_fields.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/components/attachments.rs : In `src/ui/components/attachments.rs`: compute `sanitized_name` using `sanitize_component`, show WARNING icon on sanitized mismatch, emit commands for file picking/hashing/thumbnails, and ensure edited names are sanitized/deduped

Applied to files:

  • src/ui/mod.rs
  • src/logic/eln.rs
  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/**/*.rs : Keep icon+text buttons short using format like `format!("{} Label", icon)` and reuse helpers already in `src/ui.rs`

Applied to files:

  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/**/*.rs : Use Phosphor icons via `egui_phosphor::regular::NAME` (via `RichText` or button labels) instead of embedding SVGs; the font is registered in `src/main.rs`

Applied to files:

  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/main.rs : Rust 2024 crate with entry point at `src/main.rs` which calls `app::run()` to start eframe/egui

Applied to files:

  • src/ui/mod.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Model layer: implement `mvu::AppModel` plus per-component models (`MarkdownModel`, `DateTimeModel`, `KeywordsModel`, `AttachmentsModel`)

Applied to files:

  • src/ui/mod.rs
  • src/mvu/mod.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Test business logic in `src/logic/eln.rs` independently of UI; mock file system operations where appropriate

Applied to files:

  • src/ui/mod.rs
  • src/logic/eln.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/**/*.rs : Use `rfd` crate for cross-platform native file dialog operations

Applied to files:

  • Cargo.toml
  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Align archive structure and metadata with the ELN File Format specification: https://raw.githubusercontent.com/TheELNConsortium/TheELNFileFormat/refs/heads/master/SPECIFICATION.md

Applied to files:

  • src/logic/eln.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Use `cargo test` for unit and integration coverage; colocate simple unit tests with modules and broader scenarios under `tests/`

Applied to files:

  • src/logic/eln.rs
  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Implement Commands (`PickFiles`, `HashFile`, `LoadThumbnail`, `SaveArchive`) with results feeding back as messages (`AttachmentsMsg::FilesPicked/HashComputed/ThumbnailReady`, `Msg::SaveCompleted`)

Applied to files:

  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Use `cargo run` to build and launch the desktop app, `cargo check` for fast typecheck, `cargo fmt` for formatting, `cargo clippy --all-targets --all-features` for linting, `cargo test` for testing, and `cargo build --release` for distribution

Applied to files:

  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Use `///` for public items and `//!` for module-level Rustdoc; start with one-line summary ending with period, structure with `#` headings (`# Examples`, `# Errors`, `# Panics`, `# Safety`, `# Performance`)

Applied to files:

  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Include small, runnable Rustdoc examples marked `no_run`/`ignore` when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly

Applied to files:

  • AGENTS.md
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to {src/**/*.rs,tests/**/*.rs} : Name tests after behavior (e.g., `submits_trimmed_input`) and keep them deterministic

Applied to files:

  • AGENTS.md
🧬 Code graph analysis (3)
src/ui/mod.rs (1)
src/ui/components/extra_fields.rs (1)
  • view (480-533)
src/ui/components/extra_fields.rs (1)
src/models/extra_fields.rs (5)
  • validate_field (102-152)
  • value (124-124)
  • value (134-134)
  • s (254-254)
  • s (285-285)
src/models/extra_fields.rs (2)
src/logic/eln.rs (2)
  • as_str (66-71)
  • field (479-481)
src/ui/components/extra_fields.rs (3)
  • default (36-48)
  • fields (52-54)
  • groups (56-58)
🔇 Additional comments (23)
src/models/mod.rs (1)

7-7: Make it so!

The module declaration follows the established pattern, Number One. This addition properly exposes the extra_fields domain types to the rest of the codebase. The positioning alongside attachment and keywords is logically sound.

src/ui/components/mod.rs (1)

8-8: Engage!

The UI component module is properly declared in its correct alphabetical position. This follows the established pattern for component organization aboard this vessel.

src/ui/mod.rs (4)

13-13: The crew complement is now complete.

The import of extra_fields alongside the other component modules is properly structured, Ensign.


130-132: Excellent positioning, helm.

The extra fields section is rendered before attachments with consistent spacing, maintaining visual harmony in the interface. The MVU pattern is honored.


153-154: A prudent safeguard for the Federation.

The save button is now properly gated on both title presence and extra fields validity. The hover text clearly communicates the requirements to the user. This defensive approach prevents invalid data from reaching the archive.

Also applies to: 162-162


269-272: By the book, Number One.

The implementation follows the established MVU view pattern precisely: call the component's view function, collect messages, and wrap them into the application-level Msg::ExtraFields variant. This mirrors the approach used for keywords and attachments. As per the coding guidelines, the view layer remains side-effect free.

Cargo.toml (2)

16-16: A logical choice for the mission.

The serde derive feature enables declarative serialization for the extra fields types. This is the standard approach across the Rust ecosystem.


29-31: Dependency additions are well-chosen.

The three new crates serve their purposes effectively:

  • url: URL parsing with minimal feature set
  • uuid: UUID v4 generation for PropertyValue identifiers
  • email_address: Lightweight email validation (v0.2.9 is current with no known security vulnerabilities)

The email_address crate is actively maintained, last updated October 2025, with no reported CVEs or RustSec advisories. Version constraint "0.2" appropriately allows patch updates.

AGENTS.md (2)

36-42: The ship's charter has been updated, Captain's log.

This documentation codifies the MVU layering discipline that maintains order throughout the codebase. The guidance is clear:

  • UI components emit messages only
  • Validation lives in models or update functions
  • IO occurs solely in run_command

This will serve future crew members well.


80-80: Supplemental entry noted.

The documentation of the extra fields export format—PropertyValue nodes with pv:// URIs, variableMeasured linkage, and the reconstructed elabftw_metadata blob—provides essential context for anyone maintaining the RO-Crate logic.

src/mvu/mod.rs (10)

10-10: Systems integration proceeding smoothly.

The import of domain types (ExtraField, ExtraFieldGroup) from models and UI component types (ExtraFieldsCommand, ExtraFieldsModel, ExtraFieldsMsg) follows the established separation of concerns. The models provide pure data, the UI component handles view logic.

Also applies to: 16-18


37-38: The model now contains the extra fields state.

The addition of extra_fields: ExtraFieldsModel to AppModel follows the established pattern alongside attachments, keywords, and datetime. The Rustdoc comment succinctly describes its purpose.


69-69: New message and command variants registered.

Msg::ExtraFields and Command::PickExtraFieldsFile extend the MVU vocabulary appropriately. These mirror the patterns established for attachments.

Also applies to: 78-78


98-100: The payload carries the extra fields cargo.

Adding extra_fields and extra_groups to SavePayload ensures the validated data reaches the archive writer. The types align with the domain model.


172-182: Message routing is textbook, Ensign.

The handler delegates to the component's update function, surfaces any events, and translates component-level commands to application-level commands. This maintains the layered architecture documented in AGENTS.md.


206-232: The file import procedure is sound.

The command handler:

  1. Opens a file dialog with appropriate JSON filter
  2. Reads file contents
  3. Parses via the domain parser
  4. Emits appropriate success/failure/cancellation messages

Error handling covers both file I/O failures and parse failures with distinct user-facing messages. This follows the MVU pattern where IO occurs exclusively in run_command.


256-257: The archive writer receives the full manifest.

Passing extra_fields and extra_groups to build_and_write_archive completes the data flow from UI through validation to archive generation.


321-322: Payload assembly complete.

The extra_fields and extra_groups are cloned into the payload. Given these are Vec types containing relatively small data structures, the clone is acceptable.


445-524: A thorough battery of tests, Lieutenant.

The validation tests cover:

  • Invalid and valid URL fields
  • Invalid and valid number fields
  • Invalid and valid integer fields (using Items and Users kinds)

Each test properly sets up the model state, invokes validation, and asserts the expected outcome. The error message assertions verify the user-facing text.


526-600: Well-constructed test auxiliaries.

The helper functions add_url_field and add_typed_field encapsulate the multi-step process of adding fields via the MVU flow. The assertions that no commands are enqueued verify the side-effect-free nature of these operations.

The tests are named after behavior as required by the coding guidelines.

src/ui/components/extra_fields.rs (1)

51-94: Robust MVU wiring and tests for extra fields look solid.

The orchestration of ExtraFieldsModel, the message/command/event types, and the modal logic is cohesive, and the test suite exercises imports, grouping, duplicate‑name handling, and modal flows thoroughly. From the bridge, this component appears ready for duty once the readonly handling is aligned as noted above.

Please rerun the UI flow once the readonly fix is in place to confirm that imported readonly fields from eLabFTW are rendered inert but still visible.

Also applies to: 273-333, 939-1097, 1099-1640

src/logic/eln.rs (1)

200-221: Extra fields export wiring into RO‑Crate graph is coherent and well‑tested.

The way build_extra_fields_export feeds variableMeasured, injects the elabftw_metadata PropertyValue, and how the test reconstructs and inspects the graph gives me confidence that clients expecting eLabFTW‑style metadata will find what they need. An elegant piece of engineering, Number One.

Also applies to: 253-257, 589-677

src/models/extra_fields.rs (1)

15-73: Extra field model, parsing, and validation form a solid foundation.

The ExtraFieldKind mapping, ExtraField shape, validate_field logic (including URL/number/entity/email checks), and the pure parse_elabftw_extra_fields pipeline all line up cleanly with the UI component and RO‑Crate export logic. The accompanying sample‑payload test gives us a good safety net. This subsystem is ready for service as‑is.

Also applies to: 75-152, 209-296, 298-330

@Athemis
Copy link
Copy Markdown
Owner Author

Athemis commented Dec 2, 2025

@coderabbitai help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 2, 2025

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

CodeRabbit commands

These commands are invoked using PR/Issue comments.

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai evaluate custom pre-merge check --instructions <custom-checks-instructions> --name <custom-checks-title> [--mode <error|warning>] to validate the custom pre-merge checks instructions. Defaults to error when --mode is omitted.
    • @coderabbitai ignore pre-merge checks to override pre-merge checks and get an approval on PR.
    • @coderabbitai run pre-merge checks to run pre-merge checks on the pull request.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve to resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit configuration file (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, support, documentation and community

  • Visit our status page to check the current availability of CodeRabbit.
  • Create a ticket on our support page for assistance with any issues or questions.
  • Visit our documentation site for detailed information on how to use CodeRabbit.
  • Join our Discord community to connect with other users and get help from the community.
  • Follow us on X/Twitter for updates and announcements.

Docstrings generation was requested by @Athemis.

* #4 (comment)

The following files were modified:

* `src/logic/eln.rs`
* `src/models/extra_fields.rs`
* `src/mvu/mod.rs`
* `src/ui/components/extra_fields.rs`
* `src/ui/mod.rs`
Copy link
Copy Markdown
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: 4

♻️ Duplicate comments (2)
src/ui/components/extra_fields.rs (2)

942-1000: Ensign, there appears to be a warp core breach in the documentation formatting.

There are excessive blank lines interleaved in the docstring for render_checkbox (lines 943-986). These appear to be unintentional and should be cleaned up. Additionally, this function still does not honor the field.readonly flag — checkboxes remain editable even when the field is marked readonly.

The readonly issue was previously flagged. Wrap the checkbox interaction in ui.add_enabled_ui(!field.readonly, ...) to disable the widget when readonly, consistent with render_number and render_text_input.


1015-1052: The options renderer also requires readonly protection, Number One.

As previously noted, render_options does not honor field.readonly. Both the multi-value checkboxes and single-value radio buttons should be wrapped in ui.add_enabled_ui(!field.readonly, ...) to prevent edits on readonly fields, matching the behavior of text and number inputs.

🧹 Nitpick comments (1)
src/mvu/mod.rs (1)

339-350: Consider adding email validation error handling.

The validation loop handles "required", "invalid_url", "invalid_number", and "invalid_integer", but does not have a specific message for "invalid_email" which is returned by validate_field for Email fields. The catch-all case will handle it, but a specific message would be more helpful.

             "invalid_url" => format!("Field '{}' must be a valid http/https URL.", field.label),
             "invalid_number" => format!("Field '{}' must be a valid number.", field.label),
             "invalid_integer" => format!("Field '{}' must be a valid integer ID.", field.label),
+            "invalid_email" => format!("Field '{}' must be a valid email address.", field.label),
             _ => format!("Field '{}' is invalid.", field.label),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 988ad38 and 712921b.

📒 Files selected for processing (5)
  • src/logic/eln.rs (12 hunks)
  • src/models/extra_fields.rs (1 hunks)
  • src/mvu/mod.rs (13 hunks)
  • src/ui/components/extra_fields.rs (1 hunks)
  • src/ui/mod.rs (6 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/mvu/mod.rs
  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
  • src/ui/mod.rs
  • src/models/extra_fields.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/mvu/mod.rs
  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
  • src/ui/mod.rs
  • src/models/extra_fields.rs
src/logic/eln.rs

📄 CodeRabbit inference engine (AGENTS.md)

In src/logic/eln.rs: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Files:

  • src/logic/eln.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/extra_fields.rs
  • src/ui/mod.rs
🧠 Learnings (15)
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/mvu/mod.rs
  • src/ui/components/extra_fields.rs
  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Model layer: implement `mvu::AppModel` plus per-component models (`MarkdownModel`, `DateTimeModel`, `KeywordsModel`, `AttachmentsModel`)

Applied to files:

  • src/mvu/mod.rs
  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Applied to files:

  • src/mvu/mod.rs
  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Implement Commands (`PickFiles`, `HashFile`, `LoadThumbnail`, `SaveArchive`) with results feeding back as messages (`AttachmentsMsg::FilesPicked/HashComputed/ThumbnailReady`, `Msg::SaveCompleted`)

Applied to files:

  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/components/attachments.rs : In `src/ui/components/attachments.rs`: compute `sanitized_name` using `sanitize_component`, show WARNING icon on sanitized mismatch, emit commands for file picking/hashing/thumbnails, and ensure edited names are sanitized/deduped

Applied to files:

  • src/mvu/mod.rs
  • src/logic/eln.rs
  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Update layer: `mvu::update` routes messages to reducers, validates save requests, and enqueues `Command`s

Applied to files:

  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/**/*.rs : Use `rfd` crate for cross-platform native file dialog operations

Applied to files:

  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Align archive structure and metadata with the ELN File Format specification: https://raw.githubusercontent.com/TheELNConsortium/TheELNFileFormat/refs/heads/master/SPECIFICATION.md

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Use `cargo test` for unit and integration coverage; colocate simple unit tests with modules and broader scenarios under `tests/`

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Test business logic in `src/logic/eln.rs` independently of UI; mock file system operations where appropriate

Applied to files:

  • src/logic/eln.rs
  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/components/extra_fields.rs
  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/components/extra_fields.rs
  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/ui/components/extra_fields.rs
  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/**/*.rs : Keep icon+text buttons short using format like `format!("{} Label", icon)` and reuse helpers already in `src/ui.rs`

Applied to files:

  • src/ui/mod.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/main.rs : Rust 2024 crate with entry point at `src/main.rs` which calls `app::run()` to start eframe/egui

Applied to files:

  • src/ui/mod.rs
🧬 Code graph analysis (5)
src/mvu/mod.rs (2)
src/ui/components/extra_fields.rs (2)
  • update (284-590)
  • default (55-67)
src/models/extra_fields.rs (4)
  • parse_elabftw_extra_fields (296-376)
  • validate_field (154-204)
  • value (176-176)
  • value (186-186)
src/logic/eln.rs (4)
src/models/attachment.rs (2)
  • assert_unique_sanitized_names (46-57)
  • new (28-42)
src/utils/hash.rs (1)
  • hash_file (17-24)
src/utils/sanitize_component.rs (1)
  • sanitize_component (17-110)
src/models/extra_fields.rs (6)
  • value (176-176)
  • value (186-186)
  • from_str (48-65)
  • as_str (68-85)
  • raw (304-308)
  • raw (309-313)
src/ui/components/extra_fields.rs (1)
src/models/extra_fields.rs (5)
  • validate_field (154-204)
  • value (176-176)
  • value (186-186)
  • s (334-334)
  • s (365-365)
src/ui/mod.rs (1)
src/ui/components/extra_fields.rs (1)
  • view (614-667)
src/models/extra_fields.rs (1)
src/ui/components/extra_fields.rs (3)
  • default (55-67)
  • fields (80-82)
  • groups (98-100)
🪛 GitHub Actions: CI
src/mvu/mod.rs

[error] 653-653: rustfmt formatting issue detected at line 653. Run 'cargo fmt' to fix formatting.

src/logic/eln.rs

[error] 843-843: rustfmt formatting issue detected at line 843. Run 'cargo fmt' to fix formatting.

src/ui/components/extra_fields.rs

[error] 2119-2119: rustfmt formatting issue detected at line 2119. Run 'cargo fmt' to fix formatting.

src/ui/mod.rs

[error] 392-392: rustfmt formatting issue detected at line 392. Run 'cargo fmt' to fix formatting.

src/models/extra_fields.rs

[error] 428-428: rustfmt formatting issue detected at line 428. Run 'cargo fmt' to fix formatting.

⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (30)
src/ui/mod.rs (4)

13-13: Make it so — import integration looks correct.

The extra_fields component is properly imported alongside the other UI components, Number One. This maintains consistency with the existing component architecture.


149-151: The extra fields section is properly wired into the central panel.

The render_extra_fields_section call is positioned logically between keywords and attachments, with appropriate spacing. The MVU message wrapping pattern is consistent with how other components (keywords, attachments) are integrated.


200-201: Excellent defensive check before engaging warp drive — I mean, saving.

The save button now requires both a non-empty title and valid extra fields. This prevents users from saving archives with invalid metadata, maintaining data integrity.


327-330: The extra fields view is correctly delegated and messages properly forwarded.

This follows the established pattern from the coding guidelines: the view produces Vec<ExtraFieldsMsg> which is then wrapped into Msg::ExtraFields for the MVU pipeline. As per the learnings, this is the correct approach for component integration.

src/ui/components/extra_fields.rs (7)

1-8: SPDX headers and module documentation are in order, Number One.

The file follows the coding guidelines with proper license headers and module-level documentation. The imports are focused and necessary.


10-20: The ExtraFieldsModel structure is well-designed for UI state management.

The model encapsulates all necessary state for the extra fields editor: field data, groups, editing state, and modal management. This aligns with the MVU architecture specified in the learnings.


122-124: The has_invalid_fields method provides a clean validation interface.

This method is correctly used by the save button in src/ui/mod.rs to gate saving when invalid fields exist. The delegation to field_invalid keeps the logic DRY.


283-590: The update function is comprehensive and handles all message variants correctly.

Engage — the MVU update logic follows the established pattern. Each message variant mutates the model appropriately and returns optional events for status feedback. The command emission for PickMetadataFile is properly handled.


614-667: The view function correctly produces messages without side effects.

As per the coding guidelines, the view layer produces Vec<ExtraFieldsMsg> which the parent composes into mvu::Msg. The collapsible header pattern is consistent with other sections.


1065-1105: Correct implementation of readonly for numeric fields.

The number input correctly uses ui.add_enabled(!disabled, ...) where disabled = field.readonly. This is the pattern that should be applied to checkbox and options renderers as well.


1256-1264: The name conflict check is case-insensitive and excludes self during edits — well reasoned.

This prevents duplicate field labels while allowing a field to keep its own name during editing. The case-insensitive comparison is appropriate for user-facing labels.

src/mvu/mod.rs (5)

37-38: The AppModel is correctly extended with extra_fields state.

The ExtraFieldsModel is properly integrated into the top-level application state, following the MVU architecture pattern established in the learnings.


191-201: The ExtraFields message routing follows the established delegation pattern.

The update function correctly delegates to the component's update, surfaces events, and translates component commands to top-level commands. This mirrors the pattern used for Attachments, maintaining consistency.


245-271: The file picker command implementation is robust with proper error handling.

The PickExtraFieldsFile command correctly handles all outcomes: successful parse, parse failure, read failure, and cancellation. Each path produces an appropriate message for the MVU loop to process.


339-350: Validation of extra fields before save is thorough and user-friendly.

The validation loop checks each field and produces descriptive error messages for different failure types (required, invalid_url, invalid_number, invalid_integer). The catch-all case handles unexpected validation codes gracefully.


565-601: The test helper add_url_field correctly exercises the ExtraFields workflow.

This helper drives the complete field creation flow through the MVU update function, ensuring the integration works correctly. The assertion that no commands are enqueued validates that the workflow doesn't trigger unexpected side effects.

src/logic/eln.rs (6)

25-26: The ELN_FORMAT_VERSION constant is properly documented and addresses prior feedback.

Excellent, Number One. The constant now clearly documents that version 103+ is required for eLabFTW's id-based variableMeasured support. This addresses the previous review comment about the hardcoded magic number.


28-36: The ExtraFieldsExport struct provides a clean abstraction for the export output.

This struct encapsulates all the components needed for RO-Crate integration: PropertyValue nodes, the metadata blob, and the variable measured IDs. Well-structured design.


324-383: The build_extra_fields_export function correctly generates eLabFTW-compatible PropertyValue nodes.

The function:

  • Uses UUID v4 for unique @id generation
  • Sets propertyID to the field label
  • Sets valueReference to the field kind
  • Includes optional unitText and description
  • Places the metadata blob first in variable_measured_ids as required

This follows the eLabFTW export structure. Engage!


403-516: The reconstruct_elabftw_metadata function faithfully recreates the eLabFTW JSON structure.

The function properly handles:

  • Groups with id and name
  • All field properties including options, units, position, required, etc.
  • Multi-value fields as arrays
  • Boolean flags only when true
  • The display_main_text setting

This ensures round-trip compatibility with eLabFTW imports.


562-590: The value_to_json function correctly handles different field types.

The function:

  • Returns arrays for multi-value fields
  • Keeps Number values as strings for eLabFTW compatibility
  • Converts Items/Experiments/Users to integers when parseable
  • Falls back to strings for other types

The _shape parameter is unused but provides API flexibility for future differentiation between Property and Metadata contexts.


706-793: The integration test thoroughly verifies the extra fields export structure.

This test validates:

  • variableMeasured contains both field and metadata PropertyValue references
  • The Detector PropertyValue has correct @type, valueReference, value, and unitText
  • The elabftw_metadata blob is present and parseable
  • The embedded metadata contains the expected field structure

Comprehensive coverage, Number One.

src/models/extra_fields.rs (8)

1-14: Module structure follows coding guidelines with proper SPDX headers and imports.

The module is organized correctly: pure data and parsing logic with no UI dependencies, as specified in the learnings for the models layer.


34-86: The ExtraFieldKind enum provides comprehensive type mapping with round-trip support.

The from_str and as_str methods ensure faithful conversion between eLabFTW string identifiers and internal enum variants. The Unknown variant gracefully handles future or custom field types.


88-112: The ExtraField struct captures all eLabFTW field properties.

All documented eLabFTW extra field properties are represented: label, kind, value(s), options, units, position, required, description, allow_multi_values, blank_value_on_duplicate, group_id, and readonly. The cmp_key method provides stable sorting.


154-204: Validation logic is comprehensive and handles all field types correctly.

The validate_field function:

  • Checks required fields first
  • Validates URLs for http/https scheme and presence of host
  • Validates numbers as f64
  • Validates Items/Experiments/Users as i64 integers
  • Validates Email using the email_address crate (addressing the prior review)
  • Returns None for other types (no validation needed)

This is precisely what we need, Number One.


296-376: The parse_elabftw_extra_fields function correctly deserializes eLabFTW metadata.

The parsing:

  • Handles both scalar and array values
  • Converts JSON numbers and strings to appropriate group_id types
  • Filters empty strings for optional fields
  • Sorts fields by position then label
  • Extracts groups with their positions

The use of BTreeMap for extra_fields ensures deterministic iteration order. Well implemented.


397-404: The value_to_string helper correctly normalizes JSON values.

Boolean true is converted to "on" which matches eLabFTW's checkbox semantics. Other types are appropriately stringified.


410-429: The test validates parsing of a realistic eLabFTW payload.

The test covers:

  • Group extraction with id and name
  • Required fields with group_id
  • Numeric fields with unit and units array
  • Position-based sorting

Good coverage for the core parsing functionality.


428-430: Engage formatting protocols — rustfmt required.

The CI pipeline indicates a formatting issue at line 428. Execute cargo fmt to resolve this, Lieutenant.

⛔ Skipped due to learnings
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run `cargo fmt` before committing
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/main.rs : Rust 2024 crate with entry point at `src/main.rs` which calls `app::run()` to start eframe/egui

Copy link
Copy Markdown
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)
src/logic/eln.rs (1)

125-136: Consolidate parameters into a configuration struct, Number One.

This function signature carries ten parameters—three beyond the Clippy threshold. As we add capabilities, the surface grows unwieldy. Introduce a parameter struct (for instance, ArchiveConfig) to group title, body, genre, keywords, body_format, performed_at, and the metadata slices (attachments, extra fields, groups). Reserve output as a standalone positional argument since it identifies the destination. This consolidation will improve call‑site readability and make future extensions more manageable.

Example refactor:

pub struct ArchiveConfig<'a> {
    pub title: &'a str,
    pub body: &'a str,
    pub attachments: &'a [Attachment],
    pub extra_fields: &'a [ExtraField],
    pub extra_groups: &'a [ExtraFieldGroup],
    pub performed_at: OffsetDateTime,
    pub genre: ArchiveGenre,
    pub keywords: &'a [String],
    pub body_format: BodyFormat,
}

pub fn build_and_write_archive(
    output: &Path,
    config: ArchiveConfig,
) -> Result<()> {
    // function body uses config.title, config.body, etc.
}

Then call sites become:

build_and_write_archive(
    &output,
    ArchiveConfig {
        title,
        body,
        attachments: &attachments,
        extra_fields: &extra_fields,
        extra_groups: &extra_groups,
        performed_at,
        genre,
        keywords: &keywords,
        body_format,
    },
)
♻️ Duplicate comments (4)
src/ui/components/extra_fields.rs (3)

987-1000: Readonly extra fields must not be editable in checkbox views.

Number One, the checkbox renderer still emits edit messages even when field.readonly is true—a read‑only extra field should be visibly disabled and must not produce ExtraFieldsMsg updates. This issue was previously flagged and marked as addressed, yet the current code shows no ui.add_enabled_ui(!field.readonly, …) wrapper.

Make it so by gating this control behind an enabled check:

 fn render_checkbox(
     ui: &mut egui::Ui,
     field: &ExtraField,
     idx: usize,
     msgs: &mut Vec<ExtraFieldsMsg>,
 ) {
     let mut checked = field.value == "on";
-    if ui.checkbox(&mut checked, "Checked").changed() {
-        msgs.push(ExtraFieldsMsg::ToggleCheckbox {
-            index: idx,
-            checked,
-        });
-    }
+    ui.add_enabled_ui(!field.readonly, |ui| {
+        if ui.checkbox(&mut checked, "Checked").changed() {
+            msgs.push(ExtraFieldsMsg::ToggleCheckbox {
+                index: idx,
+                checked,
+            });
+        }
+    });
 }

This brings checkbox behaviour in line with text/number handling and the readonly contract.


1015-1052: Readonly extra fields must not be editable in select/radio views.

Number One, the options renderer (for select and radio fields) still emits edit messages even when field.readonly is true. This violates the readonly contract and creates an inconsistent user experience. Like the checkbox renderer, this was previously flagged and marked as addressed.

Engage the enabled wrapper to disable these widgets when readonly:

 fn render_options(
     ui: &mut egui::Ui,
     field: &ExtraField,
     idx: usize,
     msgs: &mut Vec<ExtraFieldsMsg>,
 ) {
-    if field.allow_multi_values {
-        let mut chosen = if field.value_multi.is_empty() {
-            split_multi(&field.value)
-        } else {
-            field.value_multi.clone()
-        };
-        for opt in &field.options {
-            let mut is_on = chosen.contains(opt);
-            if ui.checkbox(&mut is_on, opt).changed() {
-                if is_on {
-                    chosen.push(opt.clone());
-                } else {
-                    chosen.retain(|v| v != opt);
-                }
-                msgs.push(ExtraFieldsMsg::UpdateMulti {
-                    index: idx,
-                    values: chosen.clone(),
-                });
-            }
-        }
-    } else {
-        let mut current = field.value.clone();
-        for opt in &field.options {
-            if ui.radio_value(&mut current, opt.clone(), opt).clicked() {
-                msgs.push(ExtraFieldsMsg::EditValue {
-                    index: idx,
-                    value: opt.clone(),
-                });
-            }
-        }
-    }
+    ui.add_enabled_ui(!field.readonly, |ui| {
+        if field.allow_multi_values {
+            let mut chosen = if field.value_multi.is_empty() {
+                split_multi(&field.value)
+            } else {
+                field.value_multi.clone()
+            };
+            for opt in &field.options {
+                let mut is_on = chosen.contains(opt);
+                if ui.checkbox(&mut is_on, opt).changed() {
+                    if is_on {
+                        chosen.push(opt.clone());
+                    } else {
+                        chosen.retain(|v| v != opt);
+                    }
+                    msgs.push(ExtraFieldsMsg::UpdateMulti {
+                        index: idx,
+                        values: chosen.clone(),
+                    });
+                }
+            }
+        } else {
+            let mut current = field.value.clone();
+            for opt in &field.options {
+                if ui.radio_value(&mut current, opt.clone(), opt).clicked() {
+                    msgs.push(ExtraFieldsMsg::EditValue {
+                        index: idx,
+                        value: opt.clone(),
+                    });
+                }
+            }
+        }
+    });
 }

This brings radio/checkbox option behaviour in line with text/number handling.


2119-2121: Formatting protocols require attention at the end of the test module.

The CI pipeline indicates a rustfmt issue at line 2119. Execute cargo fmt to resolve this, Ensign.

src/mvu/mod.rs (1)

650-654: Run cargo fmt to resolve the formatting issue, Lieutenant.

The CI pipeline indicates a rustfmt issue at line 653.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 712921b and da7f190.

📒 Files selected for processing (5)
  • src/logic/eln.rs (11 hunks)
  • src/models/extra_fields.rs (1 hunks)
  • src/mvu/mod.rs (13 hunks)
  • src/ui/components/extra_fields.rs (1 hunks)
  • src/ui/mod.rs (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/ui/mod.rs
  • src/models/extra_fields.rs
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/mvu/mod.rs
  • src/ui/components/extra_fields.rs
  • src/logic/eln.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/mvu/mod.rs
  • src/ui/components/extra_fields.rs
  • src/logic/eln.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/extra_fields.rs
src/logic/eln.rs

📄 CodeRabbit inference engine (AGENTS.md)

In src/logic/eln.rs: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Files:

  • src/logic/eln.rs
🧠 Learnings (14)
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/mvu/mod.rs
  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Model layer: implement `mvu::AppModel` plus per-component models (`MarkdownModel`, `DateTimeModel`, `KeywordsModel`, `AttachmentsModel`)

Applied to files:

  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run `cargo fmt` before committing

Applied to files:

  • src/mvu/mod.rs
  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Applied to files:

  • src/mvu/mod.rs
  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Implement Commands (`PickFiles`, `HashFile`, `LoadThumbnail`, `SaveArchive`) with results feeding back as messages (`AttachmentsMsg::FilesPicked/HashComputed/ThumbnailReady`, `Msg::SaveCompleted`)

Applied to files:

  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/components/attachments.rs : In `src/ui/components/attachments.rs`: compute `sanitized_name` using `sanitize_component`, show WARNING icon on sanitized mismatch, emit commands for file picking/hashing/thumbnails, and ensure edited names are sanitized/deduped

Applied to files:

  • src/mvu/mod.rs
  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Update layer: `mvu::update` routes messages to reducers, validates save requests, and enqueues `Command`s

Applied to files:

  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/**/*.rs : Use `rfd` crate for cross-platform native file dialog operations

Applied to files:

  • src/mvu/mod.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/ui/components/extra_fields.rs
  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Align archive structure and metadata with the ELN File Format specification: https://raw.githubusercontent.com/TheELNConsortium/TheELNFileFormat/refs/heads/master/SPECIFICATION.md

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Test business logic in `src/logic/eln.rs` independently of UI; mock file system operations where appropriate

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Use `cargo test` for unit and integration coverage; colocate simple unit tests with modules and broader scenarios under `tests/`

Applied to files:

  • src/logic/eln.rs
🧬 Code graph analysis (3)
src/mvu/mod.rs (2)
src/ui/components/extra_fields.rs (2)
  • update (284-590)
  • default (55-67)
src/models/extra_fields.rs (4)
  • parse_elabftw_extra_fields (296-376)
  • validate_field (154-204)
  • value (176-176)
  • value (186-186)
src/ui/components/extra_fields.rs (1)
src/models/extra_fields.rs (5)
  • validate_field (154-204)
  • value (176-176)
  • value (186-186)
  • s (334-334)
  • s (365-365)
src/logic/eln.rs (4)
src/models/attachment.rs (2)
  • assert_unique_sanitized_names (46-57)
  • new (28-42)
src/utils/hash.rs (1)
  • hash_file (17-24)
src/utils/sanitize_component.rs (1)
  • sanitize_component (17-110)
src/models/extra_fields.rs (6)
  • value (176-176)
  • value (186-186)
  • from_str (48-65)
  • as_str (68-85)
  • raw (304-308)
  • raw (309-313)
🪛 GitHub Actions: CI
src/ui/components/extra_fields.rs

[error] 942-942: empty lines after doc comment. Command: cargo clippy --all-targets --all-features -- -D warnings.

src/logic/eln.rs

[error] 125-125: this function has too many arguments (10/7). Command: cargo clippy --all-targets --all-features -- -D warnings.

⏰ 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). (1)
  • GitHub Check: Analyze (rust)

Copy link
Copy Markdown
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

♻️ Duplicate comments (1)
src/ui/components/extra_fields.rs (1)

964-977: Ensign, the readonly contract remains breached in checkbox and options renderers.

Number One, I observe that render_checkbox and render_options still emit edit messages even when field.readonly is true. The text and number renderers properly disable their widgets via ui.add_enabled(!field.readonly, ...), but these two functions allow state changes on readonly fields.

Make it so by gating these controls:

 fn render_checkbox(
     ui: &mut egui::Ui,
     field: &ExtraField,
     idx: usize,
     msgs: &mut Vec<ExtraFieldsMsg>,
 ) {
     let mut checked = field.value == "on";
-    if ui.checkbox(&mut checked, "Checked").changed() {
-        msgs.push(ExtraFieldsMsg::ToggleCheckbox {
-            index: idx,
-            checked,
-        });
-    }
+    ui.add_enabled_ui(!field.readonly, |ui| {
+        if ui.checkbox(&mut checked, "Checked").changed() {
+            msgs.push(ExtraFieldsMsg::ToggleCheckbox {
+                index: idx,
+                checked,
+            });
+        }
+    });
 }

Also applies to: 992-1029

🧹 Nitpick comments (1)
src/logic/eln.rs (1)

563-591: Consider removing the unused _shape parameter, Number One.

The ValueShape enum and _shape parameter are defined but currently unused in value_to_json. The doc comment notes it's "accepted for API compatibility," but if both Property and Metadata shapes produce identical output, the abstraction adds complexity without benefit.

If distinct serialization logic is planned for future use, retain it. Otherwise, simplify:

-fn value_to_json(field: &ExtraField, _shape: ValueShape) -> serde_json::Value {
+fn value_to_json(field: &ExtraField) -> serde_json::Value {

And remove the ValueShape enum and update call sites accordingly.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da7f190 and 9527806.

📒 Files selected for processing (3)
  • src/logic/eln.rs (11 hunks)
  • src/models/extra_fields.rs (1 hunks)
  • src/ui/components/extra_fields.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/ui/components/extra_fields.rs
  • src/models/extra_fields.rs
  • src/logic/eln.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/extra_fields.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/ui/components/extra_fields.rs
  • src/models/extra_fields.rs
  • src/logic/eln.rs
src/logic/eln.rs

📄 CodeRabbit inference engine (AGENTS.md)

In src/logic/eln.rs: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Files:

  • src/logic/eln.rs
🧠 Learnings (12)
📓 Common learnings
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/ui/components/extra_fields.rs
  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run `cargo fmt` before committing

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Use `///` for public items and `//!` for module-level Rustdoc; start with one-line summary ending with period, structure with `#` headings (`# Examples`, `# Errors`, `# Panics`, `# Safety`, `# Performance`)

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Applied to files:

  • src/models/extra_fields.rs
  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Align archive structure and metadata with the ELN File Format specification: https://raw.githubusercontent.com/TheELNConsortium/TheELNFileFormat/refs/heads/master/SPECIFICATION.md

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Test business logic in `src/logic/eln.rs` independently of UI; mock file system operations where appropriate

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Use `cargo test` for unit and integration coverage; colocate simple unit tests with modules and broader scenarios under `tests/`

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/components/attachments.rs : In `src/ui/components/attachments.rs`: compute `sanitized_name` using `sanitize_component`, show WARNING icon on sanitized mismatch, emit commands for file picking/hashing/thumbnails, and ensure edited names are sanitized/deduped

Applied to files:

  • src/logic/eln.rs
🧬 Code graph analysis (3)
src/ui/components/extra_fields.rs (2)
src/models/extra_fields.rs (5)
  • validate_field (155-205)
  • value (177-177)
  • value (187-187)
  • s (335-335)
  • s (366-366)
src/mvu/mod.rs (1)
  • update (125-213)
src/models/extra_fields.rs (1)
src/ui/components/extra_fields.rs (3)
  • default (55-67)
  • fields (80-82)
  • groups (98-100)
src/logic/eln.rs (4)
src/models/attachment.rs (2)
  • assert_unique_sanitized_names (46-57)
  • new (28-42)
src/utils/hash.rs (1)
  • hash_file (17-24)
src/utils/sanitize_component.rs (1)
  • sanitize_component (17-110)
src/models/extra_fields.rs (6)
  • value (177-177)
  • value (187-187)
  • from_str (48-65)
  • as_str (68-85)
  • raw (305-309)
  • raw (310-314)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (7)
src/ui/components/extra_fields.rs (1)

1-2098: Engage! This component is well-structured, Number One.

The MVU pattern is cleanly implemented with proper separation of model, messages, commands, and view. The test coverage is thorough, covering import flows, validation, modal lifecycle, and group management. The use of Phosphor icons aligns with the coding guidelines.

The documentation follows Rustdoc conventions with appropriate examples marked no_run where context dependencies exist.

src/logic/eln.rs (3)

25-36: The version constant documentation serves us well, Ensign.

The ELN_FORMAT_VERSION constant now includes a comment explaining its purpose for eLabFTW compatibility, addressing the previous review concern. The ExtraFieldsExport struct cleanly encapsulates the three components needed for RO-Crate graph construction.


325-384: The extra fields export logic is sound, Number One.

The build_extra_fields_export function correctly:

  • Generates unique UUIDs for each PropertyValue @id
  • Includes valueReference with the field kind for semantic typing
  • Adds optional unitText and description when present
  • Places the metadata blob first in variable_measured_ids for consistent ordering

706-794: Excellent test coverage for the extra fields integration, Ensign.

The test validates the complete round-trip: creating an archive with extra fields, extracting the RO-Crate metadata, and verifying both the individual PropertyValue nodes and the embedded elabftw_metadata blob. This ensures compatibility with eLabFTW's import expectations.

src/models/extra_fields.rs (3)

155-205: The validation logic is complete, Number One.

Email validation using the email_address crate has been properly integrated, addressing the previous review concern. The validation coverage now includes:

  • Required field checks
  • URL validation (http/https with host)
  • Number parsing
  • Integer validation for Items/Experiments/Users
  • Email format validation

297-377: The parsing implementation is robust, Ensign.

The parse_elabftw_extra_fields function handles JSON structure variations defensively:

  • Supports both numeric and string representations for group_id
  • Properly converts array values to value_multi with comma-joined value
  • Filters empty units and descriptions
  • Uses BTreeMap for deterministic field ordering before applying position-based sort

This aligns with the ELN File Format spec per the coding guidelines.


398-405: The value conversion handles checkbox semantics correctly.

The Bool(true) -> "on" mapping aligns with eLabFTW's checkbox value convention, ensuring imported checkboxes render and export correctly through the UI layer.

Copy link
Copy Markdown
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

🧹 Nitpick comments (2)
src/ui/components/extra_fields.rs (2)

573-599: Consider clarifying the last-group removal logic, Number One.

The implementation is correct, but when removing the last group (lines 576-584), the field group_id reassignment loop is effectively a no-op since g.id equals group.id (both refer to the same group that's being renamed). While this works, a comment explaining that we're preserving the group id during the rename would aid future maintainers.

Apply this diff to add a clarifying comment:

 if removing_last {
     if let Some(g) = model.groups.get_mut(idx) {
         g.name = "Default".into();
+        // Field group_ids remain unchanged since we're renaming, not removing
         for f in model.fields.iter_mut() {
             if f.group_id == Some(group.id) {
                 f.group_id = Some(g.id);
             }
         }
     }
 }

1693-1715: Duplicate test coverage detected, Ensign.

The tests remove_field_drops_entry (lines 1693-1715) and remove_field_updates_model (lines 2089-2120) both verify the same RemoveField functionality. While thorough testing is commendable, this duplication adds maintenance overhead without additional coverage value.

Consider removing the remove_field_updates_model test (lines 2089-2120) as it's redundant with the earlier remove_field_drops_entry test.

Also applies to: 2089-2120

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9527806 and c969535.

📒 Files selected for processing (1)
  • src/ui/components/extra_fields.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/ui/components/extra_fields.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/extra_fields.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/ui/components/extra_fields.rs
🧠 Learnings (5)
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run `cargo fmt` before committing

Applied to files:

  • src/ui/components/extra_fields.rs
🧬 Code graph analysis (1)
src/ui/components/extra_fields.rs (1)
src/models/extra_fields.rs (5)
  • validate_field (155-205)
  • value (177-177)
  • value (187-187)
  • s (335-335)
  • s (366-366)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (7)
src/ui/components/extra_fields.rs (7)

1-9: SPDX headers and module documentation are in order.

The SPDX headers follow the specified format precisely, and the module-level documentation provides a clear, concise summary of the component's purpose. Well done, Number One.


10-71: Data structures are well-architected for the MVU pattern.

The separation between the public ExtraFieldsModel and the internal FieldDraft is sound engineering. The documentation for FieldDraft::default() demonstrates exemplary attention to detail with its comprehensive example.


73-210: Public API methods are properly documented and well-designed.

The accessor methods and group management functions demonstrate clear separation of concerns. The documentation examples provide valuable guidance for API consumers. Excellent work, Ensign.


212-285: Message types properly implement the MVU communication protocol.

The separation of UI messages, side-effect commands, and feedback events follows the Model-View-Update architecture precisely. The message variants are comprehensive and well-structured.


603-755: View rendering functions follow the MVU pattern impeccably.

The message collection pattern is correctly implemented, and the use of Phosphor icons via egui_phosphor::regular::* adheres to the coding guidelines. The collapsible group structure provides excellent user experience.


757-951: Field rendering functions demonstrate proper separation of concerns.

The delegation pattern from render_field_value to specialized renderers is sound engineering. The visual feedback for invalid fields (lines 860-879) provides excellent user guidance.


1141-1358: Helper functions are well-crafted and properly documented.

The utility functions demonstrate attention to edge cases, particularly name_conflict with its case-insensitive comparison and editing exclusion logic. The documentation examples provide clear usage guidance.

Copy link
Copy Markdown
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

🧹 Nitpick comments (4)
src/ui/components/extra_fields.rs (4)

36-70: Rustdoc examples diverge from the actual public API, Number One.

Several doc snippets read more like sketches than compilable examples:

  • FieldDraft::default() (Line 46) uses a private type that is not in scope for consumers.
  • ExtraFieldsModel::new() and direct model.groups access (Lines 162–170) don’t correspond to the real API (no new, groups is private).
  • Test helpers make_field / make_group (Lines 1550–1593) are documented as if they were public utilities.

If doctests are ever enabled, these will fail to compile, and even as prose they may confuse readers about the supported surface.

I recommend either:

  • Marking these as non-compiling examples with ignore and clarifying in the prose that they are sketches, or
  • Updating them to use the actual public API (ExtraFieldsModel::default(), fields(), groups(), etc.), and avoiding references to private helpers in Rustdoc.

Also applies to: 154-173, 185-203, 1550-1593


287-325: Consider resetting modal edit state when new metadata is imported.

In the ImportLoaded branch of update you replace model.fields and model.groups, but leave modal_open, modal_draft, and editing_field untouched. If an import completes while a field-edit modal is open, editing_field may point past the end of the new field list; a later CommitFieldModal then silently does nothing for edits.

To keep the model’s invariants crisp and avoid surprising “save did nothing” behaviour, consider clearing modal state here:

        ExtraFieldsMsg::ImportLoaded {
            mut fields,
            groups,
            source,
        } => {
            fields.sort_by(|a, b| a.cmp_key().cmp(&b.cmp_key()));
            model.fields = fields;
            model.groups = groups;
            model.editing_group = None;
            model.editing_group_buffer.clear();
+           model.modal_open = false;
+           model.modal_draft = None;
+           model.editing_field = None;
            Some(ExtraFieldsEvent {
                message: format!(
                    "Imported {} field(s) from {}",
                    model.fields.len(),
                    source.display()
                ),
                is_error: false,
            })
        }

1252-1260: Simplify name_conflict’s case-insensitive comparison.

Here you lower‑case the candidate label into key and then call eq_ignore_ascii_case against it:

let key = label.trim().to_lowercase();
…
f.label.trim().eq_ignore_ascii_case(&key)

Since eq_ignore_ascii_case already performs a case‑insensitive comparison, pre‑lowercasing is redundant and slightly obscures intent.

A clearer, behaviour‑equivalent variant would be:

-fn name_conflict(model: &ExtraFieldsModel, label: &str, editing: Option<usize>) -> bool {
-    let key = label.trim().to_lowercase();
-    if key.is_empty() {
+fn name_conflict(model: &ExtraFieldsModel, label: &str, editing: Option<usize>) -> bool {
+    let key = label.trim();
+    if key.is_empty() {
         return false;
     }
     model.fields.iter().enumerate().any(|(idx, f)| {
-        idx != editing.unwrap_or(usize::MAX) && f.label.trim().eq_ignore_ascii_case(&key)
+        idx != editing.unwrap_or(usize::MAX) && f.label.trim().eq_ignore_ascii_case(key)
     })
 }

This keeps the same semantics while reading more directly.


1948-2088: Group-removal tests are thorough; consider tightening naming for clarity.

The trio of tests around group removal and default reassignment (remove_group_clears_field_group_ids, removing_last_group_recreates_default_and_reassigns, removing_only_group_renames_to_default) collectively give strong coverage of the different branches in RemoveGroup.

However, removing_last_group_recreates_default_and_reassigns now exercises the same “single group renamed to Default, id preserved” behaviour as removing_only_group_renames_to_default, which can be confusing when reading the suite.

You might consider:

  • Renaming or consolidating these tests to make the distinction between “rename only group to Default” and “remove one of several groups, reassign to Default group” more explicit, or
  • Folding overlapping assertions into a single parametrised-style test.

Not a blocker, but it will help future readers understand the intended invariants at a glance.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c969535 and ccf1824.

📒 Files selected for processing (1)
  • src/ui/components/extra_fields.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/ui/components/extra_fields.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/extra_fields.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/ui/components/extra_fields.rs
🧠 Learnings (5)
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run `cargo fmt` before committing

Applied to files:

  • src/ui/components/extra_fields.rs
🧬 Code graph analysis (1)
src/ui/components/extra_fields.rs (1)
src/models/extra_fields.rs (5)
  • validate_field (155-205)
  • value (177-177)
  • value (187-187)
  • s (335-335)
  • s (366-366)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (1)
src/ui/components/extra_fields.rs (1)

603-755: Readonly semantics and view wiring are consistent and well-executed.

The view / render_fields pipeline cleanly follows the component pattern—returning Vec<ExtraFieldsMsg> from view and leaving composition into higher‑level Msg to the caller. Within the value renderers:

  • render_checkbox, render_options, render_number, and render_text_input all gate interactions through ui.add_enabled_ui / ui.add_enabled, ensuring readonly fields are visibly disabled and never emit state‑changing messages.
  • Multi‑value select logic keeps value_multi and value in sync via UpdateMulti, matching the validation model.

This aligns the UI closely with the readonly contract and the MVU separation of concerns. Make it so.

Based on learnings, this keeps the view layer contained in the component and delegates message composition to the app shell.

Also applies to: 939-1046, 1113-1131

Copy link
Copy Markdown
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

🧹 Nitpick comments (2)
src/ui/components/extra_fields.rs (2)

73-210: Doc examples for ExtraFieldsModel diverge from the actual API.

Number One, several examples here reference API that does not exist or is not publicly accessible:

  • lowest_position_group_id’s example calls ExtraFieldsModel::new() and assumes a public groups field.
  • ensure_default_group’s example also inspects model.groups, which is private in the current definition.
  • display_group_name’s example constructs a model using struct literal syntax, again relying on fields/groups being public.

These will not compile as doctests unless you both (a) expose those fields and constructors, and (b) re-export the types at the crate root.

I recommend either:

  • Updating the examples to use the actual public surface (ExtraFieldsModel::default(), fields()/groups() accessors, display_group_name itself, etc.), or
  • Marking such illustrative blocks as ignore and adjusting the wording so they don’t promise non‑existent APIs.

This is documentation polish, not a blocker, but aligning the docs with the real API will avoid future confusion. Make it so.


1548-2093: Test suite is thorough; consider a small naming tweak for clarity.

The tests exercise the essential behaviours: import wiring, validation flags, group add/remove semantics (including the “last group” rename to Default), field creation/editing, duplicate‑name handling, and modal lifecycle. That is excellent coverage for this module.

One minor nit: group_display_name_falls_back_to_id actually asserts that unknown ids fall back to "Default", not to the id itself. Renaming it to reflect the string fallback (e.g., group_display_name_unknown_is_default) would make the intent clearer, but this is purely cosmetic.

Overall, the test module strongly supports the implementation. Make it so.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ccf1824 and 67b444a.

📒 Files selected for processing (1)
  • src/ui/components/extra_fields.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/ui/components/extra_fields.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/extra_fields.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/ui/components/extra_fields.rs
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run `cargo fmt` before committing

Applied to files:

  • src/ui/components/extra_fields.rs
🧬 Code graph analysis (1)
src/ui/components/extra_fields.rs (1)
src/models/extra_fields.rs (5)
  • validate_field (155-205)
  • value (177-177)
  • value (187-187)
  • s (335-335)
  • s (366-366)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (5)
src/ui/components/extra_fields.rs (5)

10-71: ExtraFieldsModel and FieldDraft structure look solid.

The state layout is coherent, and FieldDraft::default establishes clear invariants (text defaults, Text kind, no group). This gives the update/view logic a predictable base to work from; I see no correctness issues here. Make it so.


212-604: Update logic maintains invariants; label‑uniqueness and draft handling are well done.

The ExtraFieldsMsg/ExtraFieldsCommand/ExtraFieldsEvent surfaces and update match cleanly:

  • Import flow correctly resets editing state, sorts fields by cmp_key, and emits a concise success event.
  • Draft handling is careful: label and conflict checks run before commit, DraftKindChanged is correctly constrained to the creation path, and apply_draft_to_field preserves kind for edits.
  • Multi‑value and checkbox/unit updates keep value and value_multi consistent, and group removal logic is covered by tests.

I do not see functional gaps or invariants being violated here. Well executed.


606-758: Confirm behaviour for fields without a group, lest they vanish from the view.

In render_fields, you render only fields where f.group_id == Some(group.id). Fields with group_id == None are not shown anywhere, even though display_group_name treats such cases as “Default”.

If the upstream parsing/wiring guarantees that every field always has a valid group_id pointing to an existing group (or is migrated to one via StartAddField/ensure_default_group), then this is perfectly acceptable.

If not, ungrouped fields would silently disappear from the UI. In that case, you may want to either:

  • Normalize None → a real “Default” group on import, or
  • Add a catch‑all “Ungrouped/Default” section in render_fields.

Please verify the upstream contract for group_id and adjust if needed.


841-1104: Field card rendering and readonly semantics are now consistently enforced.

The combination of render_field, render_field_value, and the specialized renderers is strong:

  • Invalid fields are clearly highlighted without hard‑coding theme colours.
  • Checkbox, select/radio, number+unit, and text inputs all honour field.readonly via add_enabled/add_enabled_ui, and only emit messages when enabled. This addresses the earlier readonly issues for booleans/options/units and keeps the contract consistent.
  • Multi‑select logic (UpdateMulti plus value_multi/value synchronization) is straightforward and side‑effect free outside of the intended updates.

From a UI and state‑management perspective, this segment is in good order.


1136-1363: Helper functions cleanly centralize validation and transformation.

field_hint, kind_label, all_kinds, split_multi, name_conflict, field_invalid, trimmed_or_none, and apply_draft_to_field form a tidy utility layer:

  • Validation is correctly delegated to validate_field, ensuring UI and model agree on what “invalid” means.
  • name_conflict’s case‑insensitive, trimmed comparison with an optional exclusion index is exactly what the modal logic needs.
  • apply_draft_to_field respects trimming, optional description, readonly, multi‑values, and number units without overreaching into kind changes.

No functional issues detected; this is a solid foundation for the rest of the component. Engage.

Copy link
Copy Markdown
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

🧹 Nitpick comments (2)
src/ui/components/extra_fields.rs (1)

576-603: Engage clarity protocols — comment and code are at odds, Number One.

When removing the sole remaining group (the removing_last branch), the loop at lines 583–587 reassigns f.group_id to the same value it already holds, since group.id == g.id. The comment states "Field group_ids remain unchanged since we're renaming, not removing," yet the code performs a self-assignment. Consider removing this no-op loop or updating the comment to explain why the assignment is retained (perhaps for future-proofing).

             if removing_last {
                 if let Some(g) = model.groups.get_mut(idx) {
                     g.name = "Default".into();
-                    // Field group_ids remain unchanged since we're renaming, not removing
-                    for f in model.fields.iter_mut() {
-                        if f.group_id == Some(group.id) {
-                            f.group_id = Some(g.id);
-                        }
-                    }
+                    // Field group_ids remain unchanged since we only rename the group.
                 }
             }
src/logic/eln.rs (1)

519-521: A minor course correction is in order, Ensign.

There appear to be two summary lines in the doc comment at lines 519–520. Rustdoc expects a single one-line summary followed by additional description. Remove one of the duplicate introductory sentences.

-/// Convert a field's value into the most appropriate JSON type.
 /// Convert an ExtraField's value into a serde_json::Value suitable for export.
 ///
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67b444a and 4e55f8a.

📒 Files selected for processing (2)
  • src/logic/eln.rs (11 hunks)
  • src/ui/components/extra_fields.rs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
src/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.rs: Include SPDX headers on Rust source files with format: SPDX-License-Identifier: MIT plus one or more SPDX-FileCopyrightText lines naming actual authors
Use /// for public items and //! for module-level Rustdoc; start with one-line summary ending with period, structure with # headings (# Examples, # Errors, # Panics, # Safety, # Performance)
Include small, runnable Rustdoc examples marked no_run/ignore when side effects exist; keep minimal and dependency-free; explain invariants, panics, and error cases explicitly
Use intra-doc links like [`TypeName`] or [`module::function`] with full paths when needed for disambiguation in Rustdoc
Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run cargo fmt before committing
Use snake_case for functions/variables/files and CamelCase for types; keep module files small and focused
Use code comments sparingly to explain intent, invariants, or non-obvious control flow; avoid restating what the code already makes clear
Use rfd crate for cross-platform native file dialog operations

Files:

  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
src/logic/eln.rs

📄 CodeRabbit inference engine (AGENTS.md)

In src/logic/eln.rs: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Files:

  • src/logic/eln.rs
{src/**/*.rs,tests/**/*.rs}

📄 CodeRabbit inference engine (AGENTS.md)

Name tests after behavior (e.g., submits_trimmed_input) and keep them deterministic

Files:

  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
src/ui/components/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

View layer: implement component view(...) -> Vec<Msg> in ui/components/*; compose and wrap into mvu::Msg in ui::ElnPackApp

Files:

  • src/ui/components/extra_fields.rs
src/ui/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

src/ui/**/*.rs: Use Phosphor icons via egui_phosphor::regular::NAME (via RichText or button labels) instead of embedding SVGs; the font is registered in src/main.rs
Keep icon+text buttons short using format like format!("{} Label", icon) and reuse helpers already in src/ui.rs

Files:

  • src/ui/components/extra_fields.rs
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/logic/eln.rs : In `src/logic/eln.rs`: implement ELN RO-Crate build/write, metadata, and suggested archive name; conform to RO-Crate 1.2 and ELN File Format spec; use pre-sanitized names from attachments and avoid UI dependencies

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Align archive structure and metadata with the ELN File Format specification: https://raw.githubusercontent.com/TheELNConsortium/TheELNFileFormat/refs/heads/master/SPECIFICATION.md

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Test business logic in `src/logic/eln.rs` independently of UI; mock file system operations where appropriate

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Separate UI concerns (`src/ui.rs`) from business logic (`src/logic/eln.rs`) for maintainability and testability

Applied to files:

  • src/logic/eln.rs
  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Use `cargo test` for unit and integration coverage; colocate simple unit tests with modules and broader scenarios under `tests/`

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui/components/attachments.rs : In `src/ui/components/attachments.rs`: compute `sanitized_name` using `sanitize_component`, show WARNING icon on sanitized mismatch, emit commands for file picking/hashing/thumbnails, and ensure edited names are sanitized/deduped

Applied to files:

  • src/logic/eln.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/ui/components/**/*.rs : View layer: implement component `view(...) -> Vec<Msg>` in `ui/components/*`; compose and wrap into `mvu::Msg` in `ui::ElnPackApp`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Applies to src/ui.rs : Place UI strings directly in `src/ui.rs` within egui code; prefer short, actionable labels

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.295Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.295Z
Learning: Organize code by responsibility: pure data in `src/models/`, business/format logic in `src/logic/eln.rs`, MVU kernel in `src/mvu/`, UI composition in `src/ui/`, components in `src/ui/components/`, utilities in `src/utils/`

Applied to files:

  • src/ui/components/extra_fields.rs
📚 Learning: 2025-12-02T20:52:53.294Z
Learnt from: CR
Repo: Athemis/ELNPack PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-02T20:52:53.294Z
Learning: Applies to src/**/*.rs : Follow rustfmt defaults (4-space indent, trailing commas where appropriate); run `cargo fmt` before committing

Applied to files:

  • src/ui/components/extra_fields.rs
🧬 Code graph analysis (2)
src/logic/eln.rs (4)
src/models/attachment.rs (2)
  • assert_unique_sanitized_names (46-57)
  • new (28-42)
src/utils/hash.rs (1)
  • hash_file (17-24)
src/utils/sanitize_component.rs (1)
  • sanitize_component (17-110)
src/models/extra_fields.rs (6)
  • value (177-177)
  • value (187-187)
  • from_str (48-65)
  • as_str (68-85)
  • raw (305-309)
  • raw (310-314)
src/ui/components/extra_fields.rs (2)
src/models/extra_fields.rs (5)
  • validate_field (155-205)
  • value (177-177)
  • value (187-187)
  • s (335-335)
  • s (366-366)
src/mvu/mod.rs (1)
  • update (125-213)
⏰ 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). (1)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (9)
src/ui/components/extra_fields.rs (5)

1-20: Make it so — the foundation is sound.

The SPDX header, module documentation, and ExtraFieldsModel struct are well-structured. The separation of concerns follows the MVU pattern as specified in the coding guidelines for src/ui/components/**/*.rs.


629-682: The bridge crew has performed admirably.

The view() function correctly composes the collapsible "Metadata" section with action buttons, field list, and modal rendering. Icon usage follows the project's Phosphor conventions, and the component returns Vec<ExtraFieldsMsg> as expected by the MVU pattern specified in the coding guidelines.


979-1050: Excellent work, Ensign — the readonly protocols are now fully operational.

The checkbox renderer (lines 986–993), options renderer (lines 1021–1048), and number input (lines 1081–1102) all correctly respect field.readonly via add_enabled_ui. This addresses the previously flagged concern about read-only fields still being editable.


1384-1547: The holodeck simulation is well-calibrated.

The field edit modal correctly:

  • Disables the Save button when the label is empty or conflicts with an existing field (line 1407)
  • Only shows the kind selector during field creation, not editing (line 1429)
  • Handles all draft property changes with appropriate message emissions

The logic is sound, Captain.


1549-1604: The test suites are commendable, Number One.

The test helpers make_field and make_group reduce boilerplate effectively, and the tests are named after the behavior they verify—in accordance with the project's coding guidelines. Coverage spans import/export flows, validation, modal lifecycles, and group operations.

src/logic/eln.rs (4)

25-36: The ship's logs are now properly annotated, Captain.

The ELN_FORMAT_VERSION constant with its explanatory comment addresses the previously flagged issue about the hardcoded 103 value. The ExtraFieldsExport struct cleanly encapsulates the export artifacts.


231-287: The RO-Crate assembly proceeds according to specifications.

The extra fields are correctly integrated into the archive:

  • PropertyValue nodes are built via build_extra_fields_export (line 231–235)
  • The experiment's variableMeasured references each field's @id (lines 248–251)
  • Both the metadata blob and per-field PropertyValues are appended to the graph (lines 286–287)

This conforms to the ELN File Format spec as referenced in the coding guidelines.


404-517: The metadata reconstruction is logically sound.

The reconstruct_elabftw_metadata function produces a JSON structure with:

  • elabftw.extra_fields_groups containing group definitions
  • extra_fields map with each field's type, value, options, units, and metadata flags

This enables round-trip compatibility with eLabFTW imports, Captain. Well executed.


699-787: Excellent diagnostic protocols, Mr. Data.

The test build_and_write_archive_writes_elabftw_style_extra_fields comprehensively verifies:

  • The variableMeasured array contains both the per-field PropertyValue and the metadata blob (line 761)
  • The detector PropertyValue has correct @type, valueReference, value, and unitText (lines 771–775)
  • The embedded elabftw_metadata blob parses correctly and contains the expected field definition (lines 778–786)

This provides strong assurance of ELN format compliance.

@Athemis Athemis merged commit 5dfe567 into main Dec 3, 2025
5 checks passed
@Athemis Athemis deleted the feature/elabftw-extra-fields branch December 3, 2025 09:56
@coderabbitai coderabbitai bot mentioned this pull request Dec 3, 2025
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant