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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pinned `avalonia-src` producer submodule at commit
| `interop/` | `Avalonia.Rust` and `Avalonia.Rust.Interop` - the managed-side view-model interop layer |
| `apps/system-monitor/` | NeoHtop, a real Rust-owned system monitor with compiled AXAML presentation and a packaged NativeAOT host |
| `apps/pdf-viewer/` | A PDF Oxide-powered sample viewer with rendered page navigation and extracted text |
| `apps/hash-calculator/` | A streaming checksum utility (MD5, SHA-1, SHA-256, SHA-512, BLAKE3) with compare and copy |
| `tests/` | Host, IR, and generator test suites |
| `samples/` | `RustViewModelSample.Managed` - the sample presentation project the host consumes |
| `build/` | Vendored MSBuild configuration (versioning, signing, analyzers, xunit) |
Expand Down
5 changes: 5 additions & 0 deletions apps/hash-calculator/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[target.x86_64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]

[target.aarch64-pc-windows-msvc]
rustflags = ["-C", "target-feature=+crt-static"]
6 changes: 6 additions & 0 deletions apps/hash-calculator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.avalonia/
bin/
obj/
target/
*.user
*.suo
212 changes: 212 additions & 0 deletions apps/hash-calculator/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions apps/hash-calculator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "hash-calculator"
version = "0.1.0"
edition = "2021"
publish = false
description = "A sample Rustolonia hash calculator for local files"

[dependencies]
avalonia = { path = "../../rust/avalonia" }
blake3 = "1"
digest = "0.10"
hex = "0.4"
md-5 = "0.10"
sha1 = "0.10"
sha2 = "0.10"

[workspace]
50 changes: 50 additions & 0 deletions apps/hash-calculator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Rustolonia hash calculator

This sample is a sibling external consumer under `apps/` that combines
Rustolonia, Avalonia and streaming checksum crates (`sha2`, `sha1`, `md-5`,
`blake3`). Rust owns file hashing, algorithm selection, digest comparison and
the file list; the generated view-model bridge exposes that state to the
compiled Avalonia presentation. Hashing runs on a dedicated worker thread and
reads files in 64 KiB chunks so large inputs keep the UI responsive.

With no arguments, the app writes a small sample file to the system temporary
directory and hashes it with SHA-256. Passing local file paths hashes those
instead. **Open files** uses Avalonia's platform picker (multi-select). Toggle
MD5, SHA-1, SHA-256, SHA-512 and BLAKE3 independently. Paste an expected digest
to mark matching files; **Copy** / **Copy report** write hex to the clipboard.

## Build on Windows x64

From the repository root:

```powershell
pwsh ./rust/build-app.ps1 `
-ProducerRoot ./avalonia-src `
-Manifest ./apps/hash-calculator/avalonia-app.json `
-UpdateLockFile
```

The portable bundle is written to `apps/hash-calculator/artifacts/win-x64`.
Subsequent locked builds omit `-UpdateLockFile`.

## Tests

Run the Rust hashing tests:

```powershell
Push-Location ./apps/hash-calculator
cargo test --locked
Pop-Location
```

After building, the Windows UI Automation smoke test exercises startup sample
generation, SHA-256 output, enabling MD5, compare-against-digest and natural
shutdown:

```powershell
powershell.exe -NoProfile -STA -ExecutionPolicy Bypass `
-File ./apps/hash-calculator/tests/test-bundle.ps1
```

The app is a portable Win32 GUI bundle, not an installer. File-association
metadata snippets are under `file-associations/`.
15 changes: 15 additions & 0 deletions apps/hash-calculator/avalonia-app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": 1,
"presentationProject": "managed/Consumer.Presentation.csproj",
"viewModelIr": "view-model.ir.json",
"generatedAdaptersDirectory": "managed/Generated",
"generatedRegistryFile": "generated/RustViewRegistry.g.cs",
"generatedRustFile": "generated/generated_view_models.rs",
"generatedContractFile": "generated/view-model.contract.md",
"cargoManifest": "Cargo.toml",
"packageName": "hash-calculator",
"binary": "hash-calculator",
"rid": "win-x64",
"configuration": "Release",
"outputDirectory": "artifacts/win-x64"
}
38 changes: 38 additions & 0 deletions apps/hash-calculator/file-associations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# File type association metadata

These are packaging metadata snippets, not installers. They register a document
type so the desktop shell launches this application with the selected file path
as a command-line argument; the runtime side of "open with" is already wired,
because `App::run` forwards this process's arguments to the managed desktop
lifetime and `AppScope::activation_items()` returns them normalized (see
`rust/DESKTOP_FILES.md` in the pinned Avalonia producer checkout).

Deliberately out of scope here: MSIX packaging, `.msi`/`.pkg`/`.deb`/`.rpm`
installers, notarization, and any store submission. Those belong to whatever
distribution channel a consumer chooses; this workflow ships a deterministic
per-RID directory (see `PRODUCTIZATION.md`).

Replace `hash-calculator` (already substituted by `new-app`), the
extension `.myapp`, and the install path before shipping any of these.

| Platform | File | Applied by |
| --- | --- | --- |
| Windows | `windows-file-association.reg` | Your installer writing the same keys, or `reg import` for local testing. |
| Linux | `linux-desktop-entry.desktop`, `linux-mime-type.xml` | `desktop-file-install` / `xdg-mime install` from your package's post-install step. |
| macOS | `macos-Info.plist.snippet` | Merged into the `.app` bundle's `Info.plist`. |

## Verifying the runtime side

Once an association is registered, launching a file through the shell reaches
Rust as an activation item:

```rust
for item in scope.activation_items()? {
// `uri()` is always present; `local_path()` may be `None` on platforms
// that hand the application a non-local document reference.
println!("open with: {} ({})", item.uri(), item.name());
}
```

macOS additionally raises later activations while the application is already
running; subscribe with `AppScope::on_activation` to receive them.
16 changes: 16 additions & 0 deletions apps/hash-calculator/file-associations/linux-desktop-entry.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[Desktop Entry]
Type=Application
Name=hash-calculator
Comment=Rustolonia hash calculator for local files
# %F passes the selected file paths as separate arguments. Use %U instead if
# your application should also receive non-local URIs; both reach Rust through
# AppScope::activation_items(), which keeps a URI when there is no local path.
Exec=/opt/hash-calculator/hash-calculator %F
Icon=hash-calculator
Terminal=false
Categories=Utility;
MimeType=application/x-hash-calculator;

# Install with (from a package post-install step, not from the application):
# desktop-file-install --dir="$HOME/.local/share/applications" this-file.desktop
# update-desktop-database "$HOME/.local/share/applications"
18 changes: 18 additions & 0 deletions apps/hash-calculator/file-associations/linux-mime-type.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
shared-mime-info declaration for hash-calculator documents.

Install with (from a package post-install step):
xdg-mime install --mode user this-file.xml
update-mime-database "$HOME/.local/share/mime"
xdg-mime default hash-calculator.desktop application/x-hash-calculator

The glob pattern must match the extension used in the .desktop entry.
-->
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-hash-calculator">
<comment>hash-calculator document</comment>
<glob pattern="*.myapp"/>
<icon name="hash-calculator"/>
</mime-type>
</mime-info>
Loading