feat: add file digest
This commit is contained in:
parent
dab0fce18d
commit
f4c6c4f6c4
@ -245,6 +245,7 @@ message FileAction {
|
|||||||
oneof union {
|
oneof union {
|
||||||
ReadDir read_dir = 1;
|
ReadDir read_dir = 1;
|
||||||
FileTransferSendRequest send = 2;
|
FileTransferSendRequest send = 2;
|
||||||
|
FileTransferSendConfirmRequest send_confirm = 9;
|
||||||
FileTransferReceiveRequest receive = 3;
|
FileTransferReceiveRequest receive = 3;
|
||||||
FileDirCreate create = 4;
|
FileDirCreate create = 4;
|
||||||
FileRemoveDir remove_dir = 5;
|
FileRemoveDir remove_dir = 5;
|
||||||
@ -262,14 +263,23 @@ message FileResponse {
|
|||||||
FileTransferBlock block = 2;
|
FileTransferBlock block = 2;
|
||||||
FileTransferError error = 3;
|
FileTransferError error = 3;
|
||||||
FileTransferDone done = 4;
|
FileTransferDone done = 4;
|
||||||
|
FileTransferDigest digest = 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message FileTransferDigest {
|
||||||
|
int32 id = 1;
|
||||||
|
sint32 file_num = 2;
|
||||||
|
uint64 last_edit_timestamp = 3;
|
||||||
|
uint64 file_size = 4;
|
||||||
|
}
|
||||||
|
|
||||||
message FileTransferBlock {
|
message FileTransferBlock {
|
||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
sint32 file_num = 2;
|
sint32 file_num = 2;
|
||||||
bytes data = 3;
|
bytes data = 3;
|
||||||
bool compressed = 4;
|
bool compressed = 4;
|
||||||
|
uint32 blk_id = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FileTransferError {
|
message FileTransferError {
|
||||||
@ -284,6 +294,15 @@ message FileTransferSendRequest {
|
|||||||
bool include_hidden = 3;
|
bool include_hidden = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message FileTransferSendConfirmRequest {
|
||||||
|
int32 id = 1;
|
||||||
|
sint32 file_num = 2;
|
||||||
|
oneof union {
|
||||||
|
bool skip = 3;
|
||||||
|
uint32 offset_blk = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
message FileTransferDone {
|
message FileTransferDone {
|
||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
sint32 file_num = 2;
|
sint32 file_num = 2;
|
||||||
|
@ -5,8 +5,10 @@ use crate::{
|
|||||||
compress::{compress, decompress},
|
compress::{compress, decompress},
|
||||||
config::{Config, COMPRESS_LEVEL},
|
config::{Config, COMPRESS_LEVEL},
|
||||||
};
|
};
|
||||||
|
use log::log;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::os::windows::prelude::*;
|
use std::os::windows::prelude::*;
|
||||||
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
use tokio::{fs::File, io::*};
|
use tokio::{fs::File, io::*};
|
||||||
|
|
||||||
pub fn read_dir(path: &PathBuf, include_hidden: bool) -> ResultType<FileDirectory> {
|
pub fn read_dir(path: &PathBuf, include_hidden: bool) -> ResultType<FileDirectory> {
|
||||||
@ -184,6 +186,11 @@ pub fn get_recursive_files(path: &str, include_hidden: bool) -> ResultType<Vec<F
|
|||||||
read_dir_recursive(&get_path(path), &get_path(""), include_hidden)
|
read_dir_recursive(&get_path(path), &get_path(""), include_hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_file_exists(file_path: &str) -> bool {
|
||||||
|
return Path::new(file_path).exists();
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TransferJob {
|
pub struct TransferJob {
|
||||||
id: i32,
|
id: i32,
|
||||||
@ -194,6 +201,7 @@ pub struct TransferJob {
|
|||||||
total_size: u64,
|
total_size: u64,
|
||||||
finished_size: u64,
|
finished_size: u64,
|
||||||
transferred: u64,
|
transferred: u64,
|
||||||
|
default_overwrite_strategy: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -220,6 +228,7 @@ fn is_compressed_file(name: &str) -> bool {
|
|||||||
|
|
||||||
impl TransferJob {
|
impl TransferJob {
|
||||||
pub fn new_write(id: i32, path: String, files: Vec<FileEntry>) -> Self {
|
pub fn new_write(id: i32, path: String, files: Vec<FileEntry>) -> Self {
|
||||||
|
println!("new write {}", path);
|
||||||
let total_size = files.iter().map(|x| x.size as u64).sum();
|
let total_size = files.iter().map(|x| x.size as u64).sum();
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
@ -231,6 +240,7 @@ impl TransferJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_read(id: i32, path: String, include_hidden: bool) -> ResultType<Self> {
|
pub fn new_read(id: i32, path: String, include_hidden: bool) -> ResultType<Self> {
|
||||||
|
println!("new read {}", path);
|
||||||
let files = get_recursive_files(&path, include_hidden)?;
|
let files = get_recursive_files(&path, include_hidden)?;
|
||||||
let total_size = files.iter().map(|x| x.size as u64).sum();
|
let total_size = files.iter().map(|x| x.size as u64).sum();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
@ -342,7 +352,7 @@ impl TransferJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn join(&self, name: &str) -> PathBuf {
|
pub fn join(&self, name: &str) -> PathBuf {
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
self.path.clone()
|
self.path.clone()
|
||||||
} else {
|
} else {
|
||||||
@ -413,6 +423,13 @@ impl TransferJob {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
pub fn set_overwrite_strategy(&mut self, overwrite_strategy: Option<bool>) {
|
||||||
|
self.default_overwrite_strategy = overwrite_strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_overwrite_strategy(&self) -> Option<bool> {
|
||||||
|
self.default_overwrite_strategy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -468,6 +485,7 @@ pub fn new_receive(id: i32, path: String, files: Vec<FileEntry>) -> Message {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_send(id: i32, path: String, include_hidden: bool) -> Message {
|
pub fn new_send(id: i32, path: String, include_hidden: bool) -> Message {
|
||||||
|
println!("new send: {},id : {}", path, id);
|
||||||
let mut action = FileAction::new();
|
let mut action = FileAction::new();
|
||||||
action.set_send(FileTransferSendRequest {
|
action.set_send(FileTransferSendRequest {
|
||||||
id,
|
id,
|
||||||
@ -558,3 +576,27 @@ pub fn create_dir(dir: &str) -> ResultType<()> {
|
|||||||
std::fs::create_dir_all(get_path(dir))?;
|
std::fs::create_dir_all(get_path(dir))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_write_need_confirmation(
|
||||||
|
file_path: &str,
|
||||||
|
digest: &FileTransferDigest,
|
||||||
|
) -> ResultType<bool> {
|
||||||
|
let path = Path::new(file_path);
|
||||||
|
if path.exists() && path.is_file() {
|
||||||
|
let metadata = std::fs::metadata(path)?;
|
||||||
|
let modified_time = metadata.modified()?;
|
||||||
|
let remote_mt = Duration::from_millis(digest.last_edit_timestamp);
|
||||||
|
let local_mt = modified_time.duration_since(UNIX_EPOCH)?;
|
||||||
|
// if
|
||||||
|
// is_recv && remote_mt >= local_mt) || (!is_recv && remote_mt <= local_mt) ||
|
||||||
|
if remote_mt == local_mt && digest.file_size == metadata.len() {
|
||||||
|
// I'm recving or sending an newer modified file!
|
||||||
|
// or a
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1323,6 +1323,8 @@ pub enum Data {
|
|||||||
AddPortForward((i32, String, i32)),
|
AddPortForward((i32, String, i32)),
|
||||||
ToggleClipboardFile,
|
ToggleClipboardFile,
|
||||||
NewRDP,
|
NewRDP,
|
||||||
|
// ConfirmOverrideFile((i32, String, String, bool, bool)),
|
||||||
|
SetConfirmOverrideFile((i32, i32, bool, bool)),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -1332,6 +1334,12 @@ pub enum Key {
|
|||||||
_Raw(u32),
|
_Raw(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum OverrideStrategy {
|
||||||
|
Skip,
|
||||||
|
Overwrite,
|
||||||
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
pub static ref KEY_MAP: HashMap<&'static str, Key> =
|
pub static ref KEY_MAP: HashMap<&'static str, Key> =
|
||||||
[
|
[
|
||||||
|
@ -679,6 +679,7 @@ function confirmDelete(id ,path, is_remote) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handler.confirmDeleteFiles = function(id, i, name) {
|
handler.confirmDeleteFiles = function(id, i, name) {
|
||||||
|
stdout.println("id=" + id +", i=" +",name="+name);
|
||||||
var jt = file_transfer.job_table;
|
var jt = file_transfer.job_table;
|
||||||
var job = jt.job_map[id];
|
var job = jt.job_map[id];
|
||||||
if (!job) return;
|
if (!job) return;
|
||||||
@ -716,6 +717,28 @@ handler.confirmDeleteFiles = function(id, i, name) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler.overrideFileConfirm = function(id, file_num, to) {
|
||||||
|
var jt = file_transfer.job_table;
|
||||||
|
var job = jt.job_map[id];
|
||||||
|
stdout.println("job type: " + job.type);
|
||||||
|
stdout.println(id + path + to);
|
||||||
|
stdout.println(JSON.stringify(job));
|
||||||
|
msgbox("custom-skip", "Confirm Write Strategy", "<div .form> \
|
||||||
|
<div>" + translate('Overwrite') + translate('files') + ".</div> \
|
||||||
|
<div>" + translate('This file exists in your computer, skip or overwrite this file?') + "</div> \
|
||||||
|
<div.ellipsis style=\"font-weight: bold;\" .text>" + to + "</div> \
|
||||||
|
<div><button|checkbox(remember) {ts}>" + translate('Do this for all conflicts') + "</button></div> \
|
||||||
|
</div>", function(res=null) {
|
||||||
|
if (!res) {
|
||||||
|
jt.updateJobStatus(id, -1, "cancel");
|
||||||
|
} else if (res.skip) {
|
||||||
|
handler.set_write_override(id,file_num,false,true); //
|
||||||
|
} else {
|
||||||
|
handler.set_write_override(id,file_num,true,false); //
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function save_file_transfer_close_state() {
|
function save_file_transfer_close_state() {
|
||||||
var local_dir = file_transfer.local_folder_view.fd.path || "";
|
var local_dir = file_transfer.local_folder_view.fd.path || "";
|
||||||
var local_show_hidden = file_transfer.local_folder_view.show_hidden ? "Y" : "";
|
var local_show_hidden = file_transfer.local_folder_view.show_hidden ? "Y" : "";
|
||||||
|
116
src/ui/remote.rs
116
src/ui/remote.rs
@ -3,6 +3,7 @@ use crate::clipboard_file::*;
|
|||||||
use crate::{
|
use crate::{
|
||||||
client::*,
|
client::*,
|
||||||
common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL},
|
common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL},
|
||||||
|
VERSION,
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use clipboard::{
|
use clipboard::{
|
||||||
@ -10,10 +11,11 @@ use clipboard::{
|
|||||||
get_rx_clip_client, server_clip_file,
|
get_rx_clip_client, server_clip_file,
|
||||||
};
|
};
|
||||||
use enigo::{self, Enigo, KeyboardControllable};
|
use enigo::{self, Enigo, KeyboardControllable};
|
||||||
|
use hbb_common::fs::{get_string, is_file_exists};
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
config::{Config, LocalConfig, PeerConfig},
|
config::{self, Config, LocalConfig, PeerConfig},
|
||||||
fs, log,
|
fs, get_version_number, log,
|
||||||
message_proto::{permission_info::Permission, *},
|
message_proto::{permission_info::Permission, *},
|
||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
rendezvous_proto::ConnType,
|
rendezvous_proto::ConnType,
|
||||||
@ -216,6 +218,7 @@ impl sciter::EventHandler for Handler {
|
|||||||
fn toggle_option(String);
|
fn toggle_option(String);
|
||||||
fn get_remember();
|
fn get_remember();
|
||||||
fn peer_platform();
|
fn peer_platform();
|
||||||
|
fn set_write_override(i32,i32, bool,bool); // ,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,6 +539,22 @@ impl Handler {
|
|||||||
self.lc.read().unwrap().remember
|
self.lc.read().unwrap().remember
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_write_override(
|
||||||
|
&mut self,
|
||||||
|
job_id: i32,
|
||||||
|
file_num: i32,
|
||||||
|
is_override: bool,
|
||||||
|
remember: bool,
|
||||||
|
) -> bool {
|
||||||
|
self.send(Data::SetConfirmOverrideFile((
|
||||||
|
job_id,
|
||||||
|
file_num,
|
||||||
|
is_override,
|
||||||
|
remember,
|
||||||
|
)));
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn t(&self, name: String) -> String {
|
fn t(&self, name: String) -> String {
|
||||||
crate::client::translate(name)
|
crate::client::translate(name)
|
||||||
}
|
}
|
||||||
@ -1498,6 +1517,7 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool {
|
async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool {
|
||||||
|
println!("new msg from ui");
|
||||||
match data {
|
match data {
|
||||||
Data::Close => {
|
Data::Close => {
|
||||||
return false;
|
return false;
|
||||||
@ -1514,8 +1534,9 @@ impl Remote {
|
|||||||
allow_err!(peer.send(&msg).await);
|
allow_err!(peer.send(&msg).await);
|
||||||
}
|
}
|
||||||
Data::SendFiles((id, path, to, include_hidden, is_remote)) => {
|
Data::SendFiles((id, path, to, include_hidden, is_remote)) => {
|
||||||
|
println!("send files, is remote {}", is_remote);
|
||||||
if is_remote {
|
if is_remote {
|
||||||
log::debug!("New job {}, write to {} from remote {}", id, to, path);
|
println!("New job {}, write to {} from remote {}", id, to, path);
|
||||||
self.write_jobs
|
self.write_jobs
|
||||||
.push(fs::TransferJob::new_write(id, to, Vec::new()));
|
.push(fs::TransferJob::new_write(id, to, Vec::new()));
|
||||||
allow_err!(peer.send(&fs::new_send(id, path, include_hidden)).await);
|
allow_err!(peer.send(&fs::new_send(id, path, include_hidden)).await);
|
||||||
@ -1525,7 +1546,7 @@ impl Remote {
|
|||||||
self.handle_job_status(id, -1, Some(err.to_string()));
|
self.handle_job_status(id, -1, Some(err.to_string()));
|
||||||
}
|
}
|
||||||
Ok(job) => {
|
Ok(job) => {
|
||||||
log::debug!(
|
println!(
|
||||||
"New job {}, read {} to remote {}, {} files",
|
"New job {}, read {} to remote {}, {} files",
|
||||||
id,
|
id,
|
||||||
path,
|
path,
|
||||||
@ -1558,6 +1579,27 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Data::SetConfirmOverrideFile((id, file_num, need_override, remember)) => {
|
||||||
|
if let Some(job) = fs::get_job(id, &mut self.write_jobs) {
|
||||||
|
if remember {
|
||||||
|
job.set_overwrite_strategy(Some(need_override));
|
||||||
|
}
|
||||||
|
let mut msg = Message::new();
|
||||||
|
let mut file_action = FileAction::new();
|
||||||
|
file_action.set_send_confirm(FileTransferSendConfirmRequest {
|
||||||
|
id,
|
||||||
|
file_num,
|
||||||
|
union: if need_override {
|
||||||
|
Some(file_transfer_send_confirm_request::Union::offset_blk(0))
|
||||||
|
} else {
|
||||||
|
Some(file_transfer_send_confirm_request::Union::skip(true))
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
msg.set_file_action(file_action);
|
||||||
|
allow_err!(peer.send(&msg).await);
|
||||||
|
}
|
||||||
|
}
|
||||||
Data::RemoveDirAll((id, path, is_remote)) => {
|
Data::RemoveDirAll((id, path, is_remote)) => {
|
||||||
let sep = self.handler.get_path_sep(is_remote);
|
let sep = self.handler.get_path_sep(is_remote);
|
||||||
if is_remote {
|
if is_remote {
|
||||||
@ -1784,7 +1826,11 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
Some(message::Union::file_response(fr)) => match fr.union {
|
Some(message::Union::file_response(fr)) => match fr.union {
|
||||||
Some(file_response::Union::dir(fd)) => {
|
Some(file_response::Union::dir(fd)) => {
|
||||||
|
println!("file_response is dir: {}", fd.path);
|
||||||
let entries = fd.entries.to_vec();
|
let entries = fd.entries.to_vec();
|
||||||
|
for entry in &entries {
|
||||||
|
println!("dir file: {}", entry.name);
|
||||||
|
}
|
||||||
let mut m = make_fd(fd.id, &entries, fd.id > 0);
|
let mut m = make_fd(fd.id, &entries, fd.id > 0);
|
||||||
if fd.id <= 0 {
|
if fd.id <= 0 {
|
||||||
m.set_item("path", fd.path);
|
m.set_item("path", fd.path);
|
||||||
@ -1796,15 +1842,77 @@ impl Remote {
|
|||||||
job.files = entries;
|
job.files = entries;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(file_response::Union::digest(digest)) => {
|
||||||
|
if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) {
|
||||||
|
if let Some(file) = job.files().get(digest.file_num as usize) {
|
||||||
|
let write_path = get_string(&job.join(&file.name));
|
||||||
|
let overwrite_strategy = job.default_overwrite_strategy();
|
||||||
|
match fs::is_write_need_confirmation(&write_path, &digest) {
|
||||||
|
Ok(res) => {
|
||||||
|
if res {
|
||||||
|
// need confirm
|
||||||
|
if overwrite_strategy.is_none() {
|
||||||
|
self.handler.call(
|
||||||
|
"overrideFileConfirm",
|
||||||
|
&make_args!(
|
||||||
|
digest.id,
|
||||||
|
digest.file_num,
|
||||||
|
write_path
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let mut msg = Message::new();
|
||||||
|
let mut file_action = FileAction::new();
|
||||||
|
file_action
|
||||||
|
.set_send_confirm(FileTransferSendConfirmRequest {
|
||||||
|
id: digest.id,
|
||||||
|
file_num: digest.file_num,
|
||||||
|
union: Some(
|
||||||
|
if overwrite_strategy.unwrap() {
|
||||||
|
file_transfer_send_confirm_request::Union::offset_blk(0)
|
||||||
|
} else {
|
||||||
|
file_transfer_send_confirm_request::Union::skip(true)
|
||||||
|
},
|
||||||
|
),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
msg.set_file_action(file_action);
|
||||||
|
allow_err!(peer.send(&msg).await);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// file with digest need send
|
||||||
|
let mut msg = Message::new();
|
||||||
|
let mut file_action = FileAction::new();
|
||||||
|
file_action
|
||||||
|
.set_send_confirm(FileTransferSendConfirmRequest {
|
||||||
|
id: digest.id,
|
||||||
|
file_num: digest.file_num,
|
||||||
|
union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
msg.set_file_action(file_action);
|
||||||
|
allow_err!(peer.send(&msg).await);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("error recving digest: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(file_response::Union::block(block)) => {
|
Some(file_response::Union::block(block)) => {
|
||||||
|
println!("file response block, file num: {}", block.file_num);
|
||||||
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
||||||
if let Err(_err) = job.write(block, None).await {
|
if let Err(_err) = job.write(block, None).await {
|
||||||
// to-do: add "skip" for writing job
|
// to-do: add "skip" for writing job
|
||||||
|
println!("error: {}", _err);
|
||||||
}
|
}
|
||||||
self.update_jobs_status();
|
self.update_jobs_status();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(file_response::Union::done(d)) => {
|
Some(file_response::Union::done(d)) => {
|
||||||
|
println!("file response done");
|
||||||
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) {
|
||||||
job.modify_time();
|
job.modify_time();
|
||||||
fs::remove_job(d.id, &mut self.write_jobs);
|
fs::remove_job(d.id, &mut self.write_jobs);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user