commit 846ed07922a80dea9c8c1daa45954325f01b28b2 Author: liyp Date: Tue Aug 25 22:13:25 2026 -0400 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2726850 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# 编译输出目录 +/target/ + +# 依赖锁文件 +Cargo.lock + +# 本地环境配置文件 +.env + +# 编辑器和 IDE 产生的文件 +.idea/ +.vscode/ +*.iml +*.ipr +*.iws + +# 日志文件 +*.log + +# 临时文件 +temp/ +tmp/ + +# 覆盖配置文件 +Cargo.toml.orig \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9e61a05 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "VoiletTftp" +version = "1.0.0" +edition = "2024" + +[dependencies] +tokio = { version = "1.47.1", features = ["full"] } +async-tftp = "0.3.6" +slint = "1.12.1" +rfd = "0.15.4" + +[build-dependencies] +slint-build = "1.12" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..b20a192 --- /dev/null +++ b/build.rs @@ -0,0 +1,3 @@ +fn main() { + slint_build::compile("components/Window.slint").unwrap(); +} \ No newline at end of file diff --git a/components/Window.slint b/components/Window.slint new file mode 100644 index 0000000..074ec35 --- /dev/null +++ b/components/Window.slint @@ -0,0 +1,87 @@ +import { Button, VerticalBox } from "std-widgets.slint"; + +export global Globals { + //启动按钮状态 + in-out property StartEnable: false; + //停止按钮状态 + in-out property StopEnable: false; +} + +export component MainWindow inherits Window { + title: "VoileTftpTools"; + icon: @image-url("../tftp.png"); + height: 180px; + width: 320px; + default-font-family: "Segoe UI"; + default-font-size: 16px; + default-font-weight: 700; + in-out property StartEnable <=> Globals.StartEnable; + in-out property StopEnable <=> Globals.StopEnable; + //选择路径回调 + callback BrowserFolder() -> string; + property SelectFolder: ""; + //启动回调 + callback StartTftp(); + //停止回调 + callback StopTftp(); + //选择路径按钮 + Rectangle { + height: 35px; + width: 95px; + x: 10px; + y: 10px; + Button { + height: parent.height; + width: parent.width; + text: "选择路径"; + clicked => { + SelectFolder = BrowserFolder(); + } + } + } + + Rectangle { + height: 35px; + width: 95px; + x: 110px; + y: 10px; + Button { + height: parent.height; + width: parent.width; + text: "启动"; + enabled: StartEnable; + clicked => { + StartTftp(); + } + } + } + + Rectangle { + height: 35px; + width: 95px; + x: 210px; + y: 10px; + Button { + height: parent.height; + width: parent.width; + text: "停止"; + enabled: StopEnable; + clicked => { + StopTftp(); + } + } + } + + Text { + x: 13px; + y: 55px; + text: "路径:"; + } + Text { + x: 60px; + y: 55px; + width: 200px; + wrap: word-wrap; // 自动换行关键属性 + text: SelectFolder; + } +} \ No newline at end of file diff --git a/logo.ico b/logo.ico new file mode 100644 index 0000000..6abbaed Binary files /dev/null and b/logo.ico differ diff --git a/resource.rc b/resource.rc new file mode 100644 index 0000000..8904c5a --- /dev/null +++ b/resource.rc @@ -0,0 +1 @@ +0001 ICON "logo.ico" \ No newline at end of file diff --git a/resource.res b/resource.res new file mode 100644 index 0000000..ab3084b Binary files /dev/null and b/resource.res differ diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9c50fa2 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,107 @@ +#![windows_subsystem = "windows"] +use async_tftp::server::TftpServerBuilder; +use async_tftp::Result; +use rfd::FileDialog; +use slint::SharedString; +use std::path::PathBuf; +use tokio::task::{AbortHandle, JoinHandle}; +use std::sync::{Arc, Mutex}; + +slint::include_modules!(); +struct ServerState { + abort_handle: Option, +} +async fn start_tftp_server(dir: PathBuf) -> Result<(JoinHandle<()>, AbortHandle)> { + let task = tokio::spawn(async move { + println!("Starting TFTP server at: {:?}", dir); + let tftpd = TftpServerBuilder::with_dir_rw(&dir) + .expect("Failed to create server") + .build() + .await + .expect("Failed to bind socket"); + + if let Err(e) = tftpd.serve().await { + eprintln!("Server error: {}", e); + } + }); + + let abort_handle = task.abort_handle(); + Ok((task, abort_handle)) +} +#[tokio::main] +async fn main() { + let ui = MainWindow::new().unwrap(); + let state = Arc::new(Mutex::new(ServerState { abort_handle: None })); + let folder_path = Arc::new(Mutex::new(String::new())); + let path_clone = folder_path.clone(); + let ui_weak = ui.as_weak(); + + //选择路径按钮的回调函数 + ui.on_BrowserFolder(move || { + let result = FileDialog::new() + .set_directory("/") + .pick_folder(); + let mut path = result.map(|p| p.to_string_lossy().into_owned()) + .unwrap_or_default(); + if path == "" { + println!("TFTP Dir Empty !"); + if let Some(ui) = ui_weak.upgrade() { + ui.set_StartEnable(false); + } + path = String::from(""); + } + else { + if let Some(ui) = ui_weak.upgrade() { + ui.set_StartEnable(true); + } + } + *path_clone.lock().unwrap() = path.clone(); // 存储到变量 + SharedString::from(path) + }); + //启动按钮的回调函数 + let state_clone = state.clone(); + let path_clone = Arc::clone(&folder_path); + let ui_weak = ui.as_weak(); + ui.on_StartTftp(move || { + let dir = PathBuf::from(&*path_clone.lock().unwrap()); + if dir.iter().count() == 0 { + println!("TFTP Dir Empty !"); + if let Some(ui) = ui_weak.upgrade() { + ui.set_StartEnable(true); + ui.set_StopEnable(false); + } + } + else { + if let Some(ui) = ui_weak.upgrade() { + ui.set_StartEnable(false); + ui.set_StopEnable(true); + } + println!("{:?}",dir); + let state = state_clone.clone(); + tokio::spawn(async move { + match start_tftp_server(dir).await { + Ok((_handle, abort_handle)) => { + println!("Server started"); + // 存储 abort_handle 以便停止 + state.lock().unwrap().abort_handle = Some(abort_handle); + } + Err(e) => eprintln!("Error: {}", e), + } + }); + } + }); + //停止按钮的回调函数 + let ui_weak = ui.as_weak(); + let state_clone = state.clone(); + ui.on_StopTftp(move || { + if let Some(ui) = ui_weak.upgrade() { + ui.set_StartEnable(true); + ui.set_StopEnable(false); + } + if let Some(handle) = state_clone.lock().unwrap().abort_handle.take() { + handle.abort(); + println!("Server stopped"); + } + }); + ui.run().unwrap(); +} \ No newline at end of file diff --git a/tftp.png b/tftp.png new file mode 100644 index 0000000..d3a5244 Binary files /dev/null and b/tftp.png differ