Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
69 changes: 58 additions & 11 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-cargo-stable-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-

- name: Install stable toolchain
- name: Install toolchain
uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e
with:
toolchain: stable
Expand Down Expand Up @@ -63,27 +63,59 @@ jobs:
linux: true
pkg_config_libdir: ""
multilib: false
toolchain: stable
build_std_flag: ""
install_target: true
- runner: windows-latest
target: x86_64-pc-windows-msvc
archive: zip
rustflags: ""
linux: false
pkg_config_libdir: ""
multilib: false
toolchain: stable
build_std_flag: ""
install_target: true
- 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.
toolchain: nightly
build_std_flag: "-Z build-std"
install_target: false
- runner: ubuntu-latest
target: i686-unknown-linux-gnu
archive: tar.gz
rustflags: ""
linux: true
pkg_config_libdir: "/usr/lib/i386-linux-gnu/pkgconfig"
multilib: true
toolchain: stable
build_std_flag: ""
install_target: true
- runner: windows-latest
target: i686-pc-windows-msvc
archive: zip
rustflags: ""
linux: false
pkg_config_libdir: ""
multilib: false
toolchain: stable
build_std_flag: ""
install_target: true
- runner: windows-latest
target: i686-win7-windows-msvc
archive: zip
rustflags: ""
linux: false
pkg_config_libdir: ""
multilib: false
toolchain: nightly
build_std_flag: "-Z build-std"
install_target: false
runs-on: ${{ matrix.runner }}
env:
TARGET: ${{ matrix.target }}
Expand All @@ -93,15 +125,30 @@ jobs:
- name: Checkout
uses: actions/checkout@v6

- name: Set safe ref name
- name: Set safe ref name (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
$safe = $env:GITHUB_REF_NAME -replace '/', '-'
Add-Content -Path $env:GITHUB_ENV -Value "SAFE_REF=$safe"

- name: Set safe ref name (Unix)
if: runner.os != 'Windows'
shell: bash
run: echo "SAFE_REF=${GITHUB_REF_NAME//\//-}" >> "$GITHUB_ENV"

- name: Install stable toolchain
uses: dtolnay/rust-toolchain@7b1c307e0dcbda6122208f10795a713336a9b35a
- name: Install toolchain
uses: dtolnay/rust-toolchain@0b1efabc08b657293548b77fb76cc02d26091c7e
with:
toolchain: stable
target: ${{ matrix.target }}
toolchain: ${{ matrix.toolchain }}

- name: Add target
if: matrix.install_target
run: rustup target add ${{ matrix.target }} --toolchain ${{ matrix.toolchain }}

- name: Add rust-src component
if: matrix.build_std_flag != ''
run: rustup component add rust-src --toolchain ${{ matrix.toolchain }}

- name: Install Linux build deps
if: matrix.linux
Expand Down Expand Up @@ -131,18 +178,18 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-cargo-${{ matrix.toolchain }}-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-

- name: Build release
run: cargo build --release --target ${{ matrix.target }}
run: cargo +${{ matrix.toolchain }} build ${{ matrix.build_std_flag }} --release --target ${{ matrix.target }}

- name: Package artifact (Linux)
if: matrix.archive == 'tar.gz'
run: |
bin_path=target/$TARGET/release/elnpack
pkg_dir=package-linux
pkg_dir=package-${{ runner.os }}
mkdir -p "$pkg_dir"
cp "$bin_path" "$pkg_dir/"
cp README.md LICENSE "$pkg_dir/"
Expand All @@ -153,7 +200,7 @@ jobs:
shell: pwsh
run: |
$binPath = "target/${env:TARGET}/release/elnpack.exe"
$pkgDir = "package-windows"
$pkgDir = "package-${env:RUNNER_OS}"
New-Item -ItemType Directory -Force -Path $pkgDir | Out-Null
Copy-Item $binPath "$pkgDir/"
Copy-Item "README.md","LICENSE" "$pkgDir/"
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