Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ jobs:
linux: false
pkg_config_libdir: ""
multilib: false
- runner: windows-latest
target: x86_64-win7-windows-msvc
archive: zip
rustflags: ""
linux: false
pkg_config_libdir: ""
multilib: false
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- runner: ubuntu-latest
target: i686-unknown-linux-gnu
archive: tar.gz
Expand Down
12 changes: 12 additions & 0 deletions src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ use eframe::egui;
use egui_phosphor::Variant;

/// Bootstrap the desktop application and run the main egui event loop.
///
/// # Errors
///
/// Propagates any failure from `eframe::run_native`, such as window creation errors.
///
/// # Examples
///
/// ```rust,ignore
/// fn main() -> eframe::Result<()> {
/// elnpack::app::run()
/// }
/// ```
pub fn run() -> eframe::Result<()> {
// Register Phosphor icon font.
let mut fonts = egui::FontDefinitions::default();
Expand Down
20 changes: 4 additions & 16 deletions src/logic/eln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,8 @@ pub fn ensure_extension(mut path: PathBuf, extension: &str) -> PathBuf {
///
/// # Examples
///
/// ```no_run
/// ```rust,ignore
/// use time::OffsetDateTime;
/// // Construct attachments and extra fields according to your application's types,
/// // then call `build_and_write_archive`.
/// let output = std::path::Path::new("example.eln");
/// let title = "My Experiment";
/// let body = "# Notes\n\nExperiment body";
Expand Down Expand Up @@ -317,7 +315,7 @@ pub fn build_and_write_archive(
///
/// # Examples
///
/// ```
/// ```rust,ignore
/// let export = build_extra_fields_export(&[], &[]).unwrap();
/// assert!(export.property_values.is_empty());
/// assert!(export.variable_measured_ids.len() >= 1); // metadata property id is always present
Expand Down Expand Up @@ -395,8 +393,7 @@ fn build_extra_fields_export(
///
/// # Examples
///
/// ```
/// // Produce minimal eLabFTW metadata with no fields or groups.
/// ```rust,ignore
/// let json = reconstruct_elabftw_metadata(&[], &[]).unwrap();
/// assert!(json.contains(r#""elabftw""#));
/// assert!(json.contains(r#""extra_fields""#));
Expand Down Expand Up @@ -525,10 +522,9 @@ fn reconstruct_elabftw_metadata(
///
/// # Examples
///
/// ```
/// ```rust,ignore
/// # use crate::models::extra_fields::{ExtraField, ExtraFieldKind};
/// # use serde_json::Value;
/// // multi-value field
/// let f_multi = ExtraField {
/// value: "".to_string(),
/// value_multi: vec!["a".into(), "b".into()],
Expand All @@ -539,7 +535,6 @@ fn reconstruct_elabftw_metadata(
/// let v = crate::logic::eln::value_to_json(&f_multi);
/// assert_eq!(v, Value::Array(vec![Value::String("a".into()), Value::String("b".into())]));
///
/// // numeric kind exported as string
/// let f_num = ExtraField {
/// value: "3.14".to_string(),
/// value_multi: Vec::new(),
Expand Down Expand Up @@ -686,13 +681,6 @@ mod tests {
/// - the experiment node's `variableMeasured` contains both a per-field `PropertyValue` and the metadata `PropertyValue`,
/// - the per-field `PropertyValue` for the "Detector" field has the expected `@type`, `valueReference`, `value`, and `unitText`,
/// - the `elabftw_metadata` blob is present and includes the "Detector" field with the expected `type` and `value`.
///
/// # Examples
///
/// ```
/// // Creates an archive with one select extra field and verifies the ro-crate metadata
/// // contains both the PropertyValue node and the elabftw_metadata blob.
/// ```
#[test]
fn build_and_write_archive_writes_elabftw_style_extra_fields() {
use tempfile::TempDir;
Expand Down
36 changes: 36 additions & 0 deletions src/models/attachment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ pub struct Attachment {

impl Attachment {
/// Construct a new attachment with pre-sanitized metadata.
///
/// The caller must provide a name that is already filesystem-safe and
/// unique within the archive.
///
/// # Examples
///
/// ```rust,ignore
/// use std::path::PathBuf;
/// use elnpack::models::attachment::Attachment;
///
/// let att = Attachment::new(
/// PathBuf::from("/tmp/note.txt"),
/// "note.txt".into(),
/// "text/plain".into(),
/// "unavailable".into(),
/// 42,
/// );
/// assert_eq!(att.sanitized_name, "note.txt");
/// ```
pub fn new(
path: PathBuf,
sanitized_name: String,
Expand All @@ -43,6 +62,23 @@ impl Attachment {
}

/// Ensure there are no duplicate archive paths produced by sanitized names.
///
/// # Errors
///
/// Returns an error when two attachments share the same `sanitized_name`.
///
/// # Examples
///
/// ```rust,ignore
/// use std::path::PathBuf;
/// use elnpack::models::attachment::{Attachment, assert_unique_sanitized_names};
///
/// let attachments = vec![
/// Attachment::new(PathBuf::from("a.txt"), "a.txt".into(), "text/plain".into(), "unavailable".into(), 1),
/// Attachment::new(PathBuf::from("b.txt"), "a.txt".into(), "text/plain".into(), "unavailable".into(), 1),
/// ];
/// assert!(assert_unique_sanitized_names(&attachments).is_err());
/// ```
pub fn assert_unique_sanitized_names(attachments: &[Attachment]) -> Result<()> {
let mut seen = HashSet::new();
for att in attachments {
Expand Down
8 changes: 4 additions & 4 deletions src/models/extra_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl ExtraFieldKind {
///
/// # Examples
///
/// ```
/// ```rust,ignore
/// use crate::models::extra_fields::ExtraFieldKind;
///
/// assert_eq!(ExtraFieldKind::from_str("text"), ExtraFieldKind::Text);
Expand Down Expand Up @@ -128,7 +128,7 @@ impl ExtraField {
///
/// # Examples
///
/// ```
/// ```rust,ignore
/// use crate::models::extra_fields::{ExtraField, ExtraFieldKind, validate_field};
///
/// let valid_number = ExtraField {
Expand Down Expand Up @@ -280,7 +280,7 @@ pub struct ExtraFieldsImport {
///
/// # Examples
///
/// ```
/// ```rust,ignore
/// let json = r#"
/// {
/// "extra_fields": {
Expand Down Expand Up @@ -386,7 +386,7 @@ pub fn parse_elabftw_extra_fields(json: &str) -> Result<ExtraFieldsImport> {
///
/// # Examples
///
/// ```
/// ```rust,ignore
/// use serde_json::Value;
///
/// assert_eq!(super::value_to_string(None), None);
Expand Down
27 changes: 27 additions & 0 deletions src/models/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ pub struct Keywords {

impl Keywords {
/// Create a keyword collection, normalizing duplicates case-insensitively.
///
/// Leading/trailing whitespace is preserved; only duplicate tokens
/// (case-insensitive) are removed, keeping the first occurrence's casing.
///
/// # Examples
///
/// ```rust,ignore
/// use elnpack::models::keywords::Keywords;
///
/// let kw = Keywords::new(vec!["DNA".into(), "dna".into(), "RNA".into()]);
/// assert_eq!(kw.items(), &["DNA", "RNA"]);
/// ```
pub fn new(items: Vec<String>) -> Self {
let mut kw = Self { items };
kw.normalize();
Expand All @@ -19,11 +31,26 @@ impl Keywords {

#[allow(dead_code)]
/// Borrow the normalized keyword slice.
///
/// # Examples
///
/// ```rust,ignore
/// let kw = elnpack::models::keywords::Keywords::new(vec!["A".into()]);
/// assert_eq!(kw.items(), &["A"]);
/// ```
pub fn items(&self) -> &[String] {
&self.items
}

/// Consume the wrapper and return the owned vector.
///
/// # Examples
///
/// ```rust,ignore
/// let kw = elnpack::models::keywords::Keywords::new(vec!["X".into()]);
/// let owned = kw.into_vec();
/// assert_eq!(owned, vec!["X".to_string()]);
/// ```
pub fn into_vec(self) -> Vec<String> {
self.items
}
Expand Down
9 changes: 3 additions & 6 deletions src/mvu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub struct SavePayload {
///
/// # Examples
///
/// ```
/// ```rust,ignore
/// let mut model = AppModel::default();
/// let mut cmds = Vec::new();
/// update(&mut model, Msg::EntryTitleChanged("New title".into()), &mut cmds);
Expand Down Expand Up @@ -220,10 +220,8 @@ pub fn update(model: &mut AppModel, msg: Msg, cmds: &mut Vec<Command>) {
///
/// # Examples
///
/// ```
/// ```rust,ignore
/// use std::path::PathBuf;
/// // Construct a command for a non-existent file to exercise the hash path that falls back
/// // to `"unavailable"` for the sha256 and `0` for size.
/// let cmd = crate::mvu::Command::HashFile { path: PathBuf::from("nonexistent"), _retry: 0 };
/// match crate::mvu::run_command(cmd) {
/// crate::mvu::Msg::Attachments(crate::mvu::AttachmentsMsg::HashComputed { sha256, size, .. }) => {
Expand Down Expand Up @@ -608,8 +606,7 @@ mod tests {
///
/// # Examples
///
/// ```
/// // inside a test:
/// ```rust,ignore
/// let mut model = AppModel::default();
/// add_typed_field(&mut model, ExtraFieldKind::Number, "42");
/// assert_eq!(model.extra_fields.fields.len(), 1);
Expand Down
Loading
Loading