mirror of
https://github.com/lights0123/n-link.git
synced 2026-08-08 10:43:29 +00:00
feat(desktop): port device registry and error type to Tauri 2
Splits the former 478-line main.rs into focused modules. Drops hashbrown in favour of std HashMap (the drain_filter API it was used for was renamed with changed semantics) and drops vendored libusb1-sys in favour of the system library, matching nlink-cli. Also removes the Task 2 scaffold's lib.rs/tauri-plugin-opener indirection (the brief's module layout is a plain binary with no lib target) and drops the now-dangling "opener:default" permission from capabilities/default.json, which otherwise fails the Tauri build script with an unresolved permission error.
This commit is contained in:
parent
4626858fa5
commit
816300fbcc
1020
desktop/src-tauri/Cargo.lock
generated
1020
desktop/src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,25 +1,37 @@
|
||||||
[package]
|
[package]
|
||||||
name = "n-link"
|
name = "n-link"
|
||||||
version = "0.1.0"
|
version = "0.1.6"
|
||||||
description = "A Tauri App"
|
description = "Free, cross-platform, CX-II compatible computer linking program for the TI-Nspire"
|
||||||
authors = ["you"]
|
authors = ["Ben Schattinger <developer@lights0123.com>"]
|
||||||
|
license = "GPL-3.0"
|
||||||
|
repository = "https://github.com/lights0123/n-link"
|
||||||
|
default-run = "n-link"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
# The `_lib` suffix may seem redundant but it is necessary
|
|
||||||
# to make the lib name unique and wouldn't conflict with the bin name.
|
|
||||||
# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519
|
|
||||||
name = "n_link_lib"
|
|
||||||
crate-type = ["staticlib", "cdylib", "rlib"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2", features = [] }
|
tauri-build = { version = "2", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2", features = [] }
|
tauri = { version = "2.11.5", features = [] }
|
||||||
tauri-plugin-opener = "2"
|
tauri-plugin-dialog = "2.7.2"
|
||||||
serde = { version = "1", features = ["derive"] }
|
tauri-plugin-shell = "2.3.5"
|
||||||
serde_json = "1"
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
serde_json = "1.0"
|
||||||
|
anyhow = "1.0"
|
||||||
|
lazy_static = "1.4"
|
||||||
|
libnspire = "0.2.3"
|
||||||
|
rusb = "0.6.4"
|
||||||
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
indicatif = "0.15"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
libnspire-sys = { path = "../../vendor/libnspire-sys" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["custom-protocol"]
|
||||||
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "n-link"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
"description": "Capability for the main window",
|
"description": "Capability for the main window",
|
||||||
"windows": ["main"],
|
"windows": ["main"],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default"
|
||||||
"opener:default"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
113
desktop/src-tauri/src/device.rs
Normal file
113
desktop/src-tauri/src/device.rs
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use libnspire::{PID, PID_CX2, VID};
|
||||||
|
use rusb::GlobalContext;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub enum DeviceState {
|
||||||
|
Open(
|
||||||
|
Arc<Mutex<libnspire::Handle<GlobalContext>>>,
|
||||||
|
libnspire::info::Info,
|
||||||
|
),
|
||||||
|
Closed,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Device {
|
||||||
|
pub name: String,
|
||||||
|
pub device: Arc<rusb::Device<GlobalContext>>,
|
||||||
|
pub state: DeviceState,
|
||||||
|
pub needs_drivers: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref DEVICES: RwLock<HashMap<(u8, u8), Device>> = RwLock::new(HashMap::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)]
|
||||||
|
pub struct DevId {
|
||||||
|
pub bus_number: u8,
|
||||||
|
pub address: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct AddDevice {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub dev: DevId,
|
||||||
|
pub name: String,
|
||||||
|
pub is_cx_ii: bool,
|
||||||
|
pub needs_drivers: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct ProgressUpdate {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub dev: DevId,
|
||||||
|
pub remaining: usize,
|
||||||
|
pub total: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct FileInfo {
|
||||||
|
pub path: String,
|
||||||
|
pub is_dir: bool,
|
||||||
|
pub date: u64,
|
||||||
|
pub size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_device(dev: Arc<rusb::Device<GlobalContext>>) -> rusb::Result<((u8, u8), Device)> {
|
||||||
|
let descriptor = dev.device_descriptor()?;
|
||||||
|
if !(descriptor.vendor_id() == VID && matches!(descriptor.product_id(), PID | PID_CX2)) {
|
||||||
|
return Err(rusb::Error::Other);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (name, needs_drivers) = match dev.open() {
|
||||||
|
Ok(handle) => (
|
||||||
|
handle.read_product_string(
|
||||||
|
handle.read_languages(Duration::from_millis(100))?[0],
|
||||||
|
&descriptor,
|
||||||
|
Duration::from_millis(100),
|
||||||
|
)?,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
Err(rusb::Error::NotSupported) | Err(rusb::Error::Access) => (
|
||||||
|
if descriptor.product_id() == PID_CX2 {
|
||||||
|
"TI-Nspire CX II"
|
||||||
|
} else {
|
||||||
|
"TI-Nspire"
|
||||||
|
}
|
||||||
|
.to_string(),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
Err(other) => return Err(other),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
(dev.bus_number(), dev.address()),
|
||||||
|
Device {
|
||||||
|
name,
|
||||||
|
device: dev,
|
||||||
|
state: DeviceState::Closed,
|
||||||
|
needs_drivers,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_open_dev(
|
||||||
|
dev: &DevId,
|
||||||
|
) -> Result<Arc<Mutex<libnspire::Handle<GlobalContext>>>, anyhow::Error> {
|
||||||
|
if let Some(dev) = DEVICES.read().unwrap().get(&(dev.bus_number, dev.address)) {
|
||||||
|
match &dev.state {
|
||||||
|
DeviceState::Open(handle, _) => Ok(handle.clone()),
|
||||||
|
DeviceState::Closed => anyhow::bail!("Device closed"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
anyhow::bail!("Failed to find device");
|
||||||
|
}
|
||||||
|
}
|
||||||
12
desktop/src-tauri/src/error.rs
Normal file
12
desktop/src-tauri/src/error.rs
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
/// Wraps any displayable error into a serde-serializable form so it can
|
||||||
|
/// cross the Tauri IPC boundary.
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct SerializedError(String);
|
||||||
|
|
||||||
|
impl<T: std::fmt::Display> From<T> for SerializedError {
|
||||||
|
fn from(f: T) -> Self {
|
||||||
|
SerializedError(f.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
|
||||||
#[tauri::command]
|
|
||||||
fn greet(name: &str) -> String {
|
|
||||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
||||||
pub fn run() {
|
|
||||||
tauri::Builder::default()
|
|
||||||
.plugin(tauri_plugin_opener::init())
|
|
||||||
.invoke_handler(tauri::generate_handler![greet])
|
|
||||||
.run(tauri::generate_context!())
|
|
||||||
.expect("error while running tauri application");
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
mod device;
|
||||||
|
mod error;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
n_link_lib::run()
|
tauri::Builder::default()
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue