mirror of
https://github.com/OMGeeky/drive_syncer.git
synced 2025-12-27 06:29:38 +01:00
implement rename & move (local & remote)
This commit is contained in:
@@ -21,7 +21,8 @@ pub use handle_flags::HandleFlags;
|
||||
use crate::fs::drive_file_provider::{
|
||||
ProviderLookupRequest, ProviderMetadataRequest, ProviderOpenFileRequest,
|
||||
ProviderReadContentRequest, ProviderReadDirRequest, ProviderReleaseFileRequest,
|
||||
ProviderRequest, ProviderResponse, ProviderSetAttrRequest, ProviderWriteContentRequest,
|
||||
ProviderRenameRequest, ProviderRequest, ProviderResponse, ProviderSetAttrRequest,
|
||||
ProviderWriteContentRequest,
|
||||
};
|
||||
use crate::google_drive::DriveId;
|
||||
use crate::{
|
||||
@@ -478,4 +479,51 @@ impl Filesystem for DriveFilesystem {
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region rename
|
||||
#[instrument(skip(_req, reply, _flags), fields(% self))]
|
||||
fn rename(
|
||||
&mut self,
|
||||
_req: &Request<'_>,
|
||||
parent: u64,
|
||||
name: &OsStr,
|
||||
new_parent: u64,
|
||||
new_name: &OsStr,
|
||||
_flags: u32,
|
||||
reply: ReplyEmpty,
|
||||
) {
|
||||
let (provider_res_tx, mut provider_rx) = tokio::sync::mpsc::channel(1);
|
||||
let parent_id = self.get_id_from_ino(parent);
|
||||
reply_error_o!(
|
||||
parent_id,
|
||||
reply,
|
||||
libc::ENOENT,
|
||||
"Failed to find drive_id for ino: {}",
|
||||
parent
|
||||
);
|
||||
let new_parent_id = self.get_id_from_ino(new_parent);
|
||||
reply_error_o!(
|
||||
new_parent_id,
|
||||
reply,
|
||||
libc::ENOENT,
|
||||
"Failed to find drive_id for ino: {}",
|
||||
new_parent
|
||||
);
|
||||
|
||||
let v = ProviderRequest::Rename(ProviderRenameRequest::new(
|
||||
name.to_os_string(),
|
||||
parent_id.clone(),
|
||||
new_name.to_os_string(),
|
||||
new_parent_id.clone(),
|
||||
provider_res_tx,
|
||||
));
|
||||
send_request!(self.file_provider_sender, v, reply);
|
||||
receive_response!(provider_rx, response, reply);
|
||||
|
||||
match_provider_response!(response, reply, ProviderResponse::Rename, {
|
||||
//
|
||||
debug!("Sending Ok.")
|
||||
reply.ok();
|
||||
});
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ use std::{
|
||||
io::SeekFrom,
|
||||
os::unix::prelude::MetadataExt,
|
||||
path::PathBuf,
|
||||
result::Result as StdResult,
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use fuser::{FileAttr, FileType};
|
||||
use google_drive3::api::StartPageToken;
|
||||
use libc::c_int;
|
||||
use tokio::{
|
||||
fs,
|
||||
fs::{File, OpenOptions},
|
||||
@@ -20,8 +22,10 @@ use tokio::{
|
||||
use tracing::{debug, error, instrument, trace, warn};
|
||||
|
||||
use crate::{
|
||||
common::VecExtension,
|
||||
fs::drive::{Change, ChangeType},
|
||||
fs::drive2::HandleFlags,
|
||||
fs::drive_file_provider::ProviderRenameRequest,
|
||||
fs::drive_file_provider::{
|
||||
FileMetadata, ProviderLookupRequest, ProviderMetadataRequest, ProviderOpenFileRequest,
|
||||
ProviderReadContentRequest, ProviderReadDirRequest, ProviderReadDirResponse,
|
||||
@@ -57,6 +61,11 @@ pub struct FileData {
|
||||
pub attr: FileAttr,
|
||||
pub is_local: bool,
|
||||
}
|
||||
impl FileData {
|
||||
fn get_id(&self) -> Option<DriveId> {
|
||||
self.metadata.id.map(|x| DriveId::from(x))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FileHandleData {
|
||||
@@ -145,6 +154,21 @@ impl DriveFileProvider {
|
||||
self.children.insert(parent_id, vec![child_id]);
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_parent_child_relation(&mut self, parent_id: DriveId, child_id: DriveId) {
|
||||
trace!(
|
||||
"removing child-parent relation for child: {:<50} and parent: {:<50}",
|
||||
child_id,
|
||||
parent_id
|
||||
);
|
||||
if let Some(parents) = self.parents.get_mut(&child_id) {
|
||||
parents.remove_first_element(&parent_id);
|
||||
}
|
||||
if let Some(children) = self.children.get_mut(&parent_id) {
|
||||
children.remove_first_element(&child_id);
|
||||
}
|
||||
}
|
||||
|
||||
//region listeners
|
||||
#[instrument(skip(self, request_reciever, command_receiver))]
|
||||
pub async fn listen(
|
||||
@@ -202,6 +226,7 @@ impl DriveFileProvider {
|
||||
ProviderRequest::ReadContent(r) => self.read_content(r).await,
|
||||
ProviderRequest::WriteContent(r) => self.write_content(r).await,
|
||||
ProviderRequest::ReadDir(r) => self.read_dir(r).await,
|
||||
ProviderRequest::Rename(r) => self.rename(r).await,
|
||||
ProviderRequest::Lookup(r) => self.lookup(r).await,
|
||||
ProviderRequest::SetAttr(r) => self.set_attr(r).await,
|
||||
_ => {
|
||||
@@ -244,29 +269,20 @@ impl DriveFileProvider {
|
||||
let name = name.unwrap();
|
||||
let parent_id = self.get_correct_id(request.parent);
|
||||
debug!("looking up {} under id {}", name, parent_id);
|
||||
let children = self.children.get(&parent_id);
|
||||
|
||||
// let mut result = vec![];
|
||||
for child in children.unwrap_or(&vec![]) {
|
||||
if let Some(child) = self.entries.get(child) {
|
||||
if child
|
||||
.metadata
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap_or(&"NO_NAME".to_string())
|
||||
.eq_ignore_ascii_case(&name)
|
||||
{
|
||||
// let response = result.push(Self::create_file_metadata_from_entry(child));
|
||||
let result = Self::create_file_metadata_from_entry(child);
|
||||
let response = ProviderResponse::Lookup(Some(result));
|
||||
return send_response!(request, response);
|
||||
}
|
||||
}
|
||||
let result = self.find_first_child_by_name(&name, &parent_id);
|
||||
|
||||
if let Some(result) = result {
|
||||
let result = Self::create_file_metadata_from_entry(result);
|
||||
let response = ProviderResponse::Lookup(Some(result));
|
||||
return send_response!(request, response);
|
||||
}
|
||||
|
||||
debug!("could not find file: {} in {}", name, parent_id);
|
||||
let response = ProviderResponse::Lookup(None);
|
||||
return send_response!(request, response);
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region read dir
|
||||
#[instrument(skip(request))]
|
||||
@@ -543,6 +559,105 @@ impl DriveFileProvider {
|
||||
let data = data.unwrap();
|
||||
send_response!(request, ProviderResponse::ReadContent(data))
|
||||
}
|
||||
//endregion
|
||||
//region rename
|
||||
|
||||
async fn rename(&mut self, request: ProviderRenameRequest) -> Result<()> {
|
||||
let original_parent = self.get_correct_id(request.original_parent.clone());
|
||||
let original_name = request.original_name.into_string();
|
||||
if let Err(e) = original_name {
|
||||
return send_error_response!(
|
||||
request,
|
||||
anyhow!("Could not convert original name into string: {:?}", e),
|
||||
libc::EIO
|
||||
);
|
||||
}
|
||||
let original_name = original_name.unwrap();
|
||||
let new_parent = self.get_correct_id(request.new_parent.clone());
|
||||
let new_name = request.new_name.into_string();
|
||||
if let Err(e) = new_name {
|
||||
return send_error_response!(
|
||||
request,
|
||||
anyhow!("Could not convert new name into string: {:?}", e),
|
||||
libc::EIO
|
||||
);
|
||||
}
|
||||
let new_name = new_name.unwrap();
|
||||
|
||||
let rename_result = self
|
||||
.rename_inner(&original_parent, &original_name, &new_parent, &new_name)
|
||||
.await;
|
||||
if let Err((msg, code)) = rename_result {
|
||||
return send_error_response!(request, anyhow!("{}", msg), code);
|
||||
}
|
||||
|
||||
send_response!(request, ProviderResponse::Rename)
|
||||
}
|
||||
|
||||
async fn rename_inner(
|
||||
&mut self,
|
||||
original_parent: &DriveId,
|
||||
original_name: &String,
|
||||
new_parent: &DriveId,
|
||||
new_name: &String,
|
||||
) -> StdResult<(), (String, c_int)> {
|
||||
let file_entry = self.find_first_child_by_name(&original_name, &original_parent);
|
||||
if file_entry.is_none() {
|
||||
return Err((format!("Could not find rename source"), libc::ENOENT));
|
||||
}
|
||||
let file_entry = file_entry.unwrap();
|
||||
|
||||
let file_id = file_entry.get_id();
|
||||
if file_id.is_none() {
|
||||
return Err((format!("Could not get id from entry"), libc::EINVAL));
|
||||
}
|
||||
let file_id = file_id.unwrap();
|
||||
|
||||
let wait_res = self
|
||||
.wait_for_running_drive_request_if_exists(&file_id)
|
||||
.await;
|
||||
if let Err(e) = wait_res {
|
||||
return Err((e.to_string(), libc::EIO));
|
||||
}
|
||||
|
||||
if self.check_id_exists(&new_parent) {
|
||||
return Err((format!("Folder does not exist"), libc::ENOENT));
|
||||
}
|
||||
|
||||
if self.does_target_name_exist_under_parent(&new_parent, &new_name) {
|
||||
return Err((format!("Target name is already used"), libc::EADDRINUSE));
|
||||
}
|
||||
|
||||
let entry = self
|
||||
.entries
|
||||
.get_mut(&file_id)
|
||||
.expect("We checked shortly before if the entry exists");
|
||||
|
||||
if original_name != new_name {
|
||||
//check if the filename has been changed and update it in the metadata and on google drive
|
||||
entry.changed_metadata.name = Some(new_name.clone());
|
||||
}
|
||||
let now = SystemTime::now();
|
||||
entry.attr.atime = now;
|
||||
entry.attr.mtime = now;
|
||||
//check if the path is changed (child-parent relationships) and modify them accordingly
|
||||
if original_parent != new_parent {
|
||||
entry.changed_metadata.parents = Some(vec![new_parent.to_string()]);
|
||||
self.remove_parent_child_relation(original_parent.clone(), file_id.clone());
|
||||
self.add_parent_child_relation(new_parent.clone(), file_id.clone());
|
||||
}
|
||||
|
||||
let upload_result = self.update_remote_metadata(file_id).await;
|
||||
if let Err(e) = upload_result {
|
||||
return Err((
|
||||
format!("Error while uploading Metadata: {:?}", e),
|
||||
libc::EREMOTEIO,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//endregion
|
||||
//region write content
|
||||
#[instrument(skip(request))]
|
||||
@@ -567,6 +682,41 @@ impl DriveFileProvider {
|
||||
//endregion
|
||||
//region request helpers
|
||||
|
||||
fn does_target_name_exist_under_parent(
|
||||
&self,
|
||||
new_parent: &&DriveId,
|
||||
new_name: &&String,
|
||||
) -> bool {
|
||||
let new_file_entry = self.find_first_child_by_name(&new_name, &new_parent);
|
||||
return new_file_entry.is_some();
|
||||
}
|
||||
fn check_id_exists(&self, id: &DriveId) -> bool {
|
||||
self.entries.contains_key(id)
|
||||
}
|
||||
|
||||
/// returns the first entry it finds with the specified name that is a child of the parent_id
|
||||
///
|
||||
/// returns ```Option::None``` if none match/the parent does not have any children
|
||||
fn find_first_child_by_name(&self, name: &String, parent_id: &DriveId) -> Option<&FileData> {
|
||||
let mut result = None;
|
||||
let children = self.children.get(&parent_id);
|
||||
for child in children.unwrap_or(&vec![]) {
|
||||
if let Some(child) = self.entries.get(child) {
|
||||
if child
|
||||
.metadata
|
||||
.name
|
||||
.as_ref()
|
||||
.unwrap_or(&"$'\\NO_NAME".to_string())
|
||||
.eq_ignore_ascii_case(&name)
|
||||
{
|
||||
result = Some(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// gets the file-handle and opens the file if it is marked for open.
|
||||
///
|
||||
/// If it is not marked for open but the file is None this returns an error
|
||||
@@ -717,6 +867,20 @@ impl DriveFileProvider {
|
||||
changes
|
||||
}
|
||||
|
||||
async fn update_remote_metadata(&self, id: DriveId) -> Result<()> {
|
||||
let file_data = self.entries.get(&id);
|
||||
if file_data.is_none() {
|
||||
return Err(anyhow!("Could not get entry with id: {}", id));
|
||||
}
|
||||
let file_data = file_data.unwrap();
|
||||
let mut metadata = file_data.changed_metadata.clone();
|
||||
Self::prepare_changed_metadata_for_upload(&id, &mut metadata);
|
||||
self.drive
|
||||
.update_file_metadata_on_drive(metadata, &file_data.metadata);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// starts a download of the specified file and puts it in the running_requests map
|
||||
///
|
||||
/// - will return an Error if another request is already running for the same id, so all callers should make sure of that
|
||||
@@ -758,7 +922,10 @@ impl DriveFileProvider {
|
||||
.entries
|
||||
.get(&id)
|
||||
.context("could not find data for id")?;
|
||||
|
||||
let mut metadata = file_data.changed_metadata.clone();
|
||||
Self::prepare_changed_metadata_for_upload(&id, &mut metadata);
|
||||
metadata.mime_type = file_data.metadata.mime_type.clone();
|
||||
|
||||
let target_path = self.construct_path(&id)?;
|
||||
debug!(
|
||||
@@ -766,9 +933,6 @@ impl DriveFileProvider {
|
||||
target_path.display(),
|
||||
metadata
|
||||
);
|
||||
metadata.id = Some(id.clone().into());
|
||||
metadata.mime_type = file_data.metadata.mime_type.clone();
|
||||
let metadata = remove_volatile_metadata(metadata);
|
||||
let handle: JoinHandle<Result<()>> = tokio::spawn(async move {
|
||||
//TODO1: only send the changed metadata over (+id), not all of it (currently only all data that could change and where changes should be written to the drive), since google drive only wants the changes
|
||||
drive
|
||||
@@ -780,6 +944,11 @@ impl DriveFileProvider {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_changed_metadata_for_upload(id: &DriveId, mut metadata: &mut DriveFileMetadata) {
|
||||
metadata.id = Some(id.clone().into());
|
||||
remove_volatile_metadata(&mut metadata);
|
||||
}
|
||||
|
||||
/// Checks if a drive request for this ID is running and if there is, waits for it.
|
||||
///
|
||||
/// After awaiting, it removes the request from the map
|
||||
@@ -834,45 +1003,52 @@ impl DriveFileProvider {
|
||||
.expect("adding the root entry has to work, otherwise nothing else works");
|
||||
let entries = self.drive.list_all_files().await?;
|
||||
for entry in entries {
|
||||
let id = &entry.id;
|
||||
if let Some(id) = id {
|
||||
let id = DriveId::from(id);
|
||||
let attr = self.create_file_attr_from_metadata(&entry);
|
||||
if attr.is_err() {
|
||||
warn!(
|
||||
"error while creating FileAttr from metadata: {:?} entry: {:?}",
|
||||
attr, entry
|
||||
);
|
||||
continue;
|
||||
}
|
||||
let attr = attr.unwrap();
|
||||
if let Some(parents) = &entry.parents {
|
||||
for parent in parents {
|
||||
let parent_id = DriveId::from(parent);
|
||||
self.add_parent_child_relation(parent_id, id.clone());
|
||||
}
|
||||
} else {
|
||||
//file is at root level
|
||||
self.add_parent_child_relation(
|
||||
self.get_correct_id(DriveId::root()),
|
||||
id.clone(),
|
||||
);
|
||||
}
|
||||
let entry_data = FileData {
|
||||
metadata: entry,
|
||||
changed_metadata: Default::default(),
|
||||
perma: false, //TODO: read the perma marker from somewhere (maybe only after all files have been checked?)
|
||||
attr,
|
||||
is_local: false,
|
||||
};
|
||||
self.entries.insert(id, entry_data);
|
||||
}
|
||||
self.add_drive_entry_to_entries(entry);
|
||||
}
|
||||
// for (i, (id, data)) in self.entries.iter().enumerate() {
|
||||
// info!("entry {:3} id: {:>40} data: {:?}", i, id, data);
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_drive_entry_to_entries(&mut self, entry: DriveFileMetadata) -> bool {
|
||||
let id = &entry.id;
|
||||
if let Some(id) = id {
|
||||
let id = DriveId::from(id);
|
||||
let attr = self.create_file_attr_from_metadata(&entry);
|
||||
if attr.is_err() {
|
||||
warn!(
|
||||
"error while creating FileAttr from metadata: {:?} entry: {:?}",
|
||||
attr, entry
|
||||
);
|
||||
return true;
|
||||
}
|
||||
let attr = attr.unwrap();
|
||||
self.add_child_parent_relations(&entry, &id);
|
||||
let entry_data = FileData {
|
||||
metadata: entry,
|
||||
changed_metadata: Default::default(),
|
||||
perma: false, //TODO: read the perma marker from somewhere (maybe only after all files have been checked?)
|
||||
attr,
|
||||
is_local: false,
|
||||
};
|
||||
self.entries.insert(id, entry_data);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn add_child_parent_relations(&mut self, entry: &DriveFileMetadata, id: &DriveId) {
|
||||
if let Some(parents) = &entry.parents {
|
||||
for parent in parents {
|
||||
let parent_id = DriveId::from(parent);
|
||||
self.add_parent_child_relation(parent_id, id.clone());
|
||||
}
|
||||
} else {
|
||||
//file is at root level
|
||||
self.add_parent_child_relation(self.get_correct_id(DriveId::root()), id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn create_file_attr_from_metadata(&self, metadata: &DriveFileMetadata) -> Result<FileAttr> {
|
||||
let kind = convert_mime_type_to_file_type(
|
||||
metadata.mime_type.as_ref().unwrap_or(&"NONE".to_string()),
|
||||
@@ -930,8 +1106,11 @@ impl DriveFileProvider {
|
||||
self.entries.insert(root_id, data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// changes alias ids like ```DriveId::root()``` into their actual IDs on the drive
|
||||
fn get_correct_id(&self, id: DriveId) -> DriveId {
|
||||
if id == DriveId::root() {
|
||||
trace!("aliasing DriveId::root() to actual root: {}", id);
|
||||
return self.alt_root_id.clone();
|
||||
}
|
||||
return id;
|
||||
@@ -961,6 +1140,53 @@ impl DriveFileProvider {
|
||||
todo!("there was a file/dir added on the remote since this ID is unknown")
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, file_change))]
|
||||
fn process_remote_file_moved(&mut self, id: &DriveId, file_change: &DriveFileMetadata) {
|
||||
if let Some(changed_parents) = &file_change.parents {
|
||||
trace!("parent was changed! {:?}", id);
|
||||
let entry = self.entries.get(&id);
|
||||
if let Some(entry) = entry {
|
||||
trace!(
|
||||
"changed_parents: {:?} before change: {:?}",
|
||||
changed_parents,
|
||||
entry.metadata.parents
|
||||
);
|
||||
if Some(changed_parents) != entry.metadata.parents.as_ref() {
|
||||
if let Some(existing_parents) = entry.metadata.parents.clone() {
|
||||
for e in existing_parents {
|
||||
let old_parent_id = self.get_correct_id(DriveId::from(&e));
|
||||
trace!("(1) converted id from {} to {}", e, old_parent_id);
|
||||
self.remove_parent_child_relation(old_parent_id, id.clone());
|
||||
}
|
||||
}
|
||||
trace!("done removing old parents");
|
||||
for new_parent in changed_parents {
|
||||
let new_parent_id = self.get_correct_id(DriveId::from(new_parent));
|
||||
trace!("(2) converted id from {} to {} ", new_parent, new_parent_id);
|
||||
self.add_parent_child_relation(new_parent_id, id.clone());
|
||||
}
|
||||
trace!("done adding new parents");
|
||||
let entry_m = self.entries.get_mut(id);
|
||||
if let Some(entry_m) = entry_m {
|
||||
entry_m.metadata.parents = file_change.parents.clone();
|
||||
}
|
||||
trace!("done modifying metadata");
|
||||
} else {
|
||||
trace!(
|
||||
"before and after are equal: {:?} == {:?}",
|
||||
Some(changed_parents),
|
||||
entry.metadata.parents
|
||||
);
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
"A remote file was moved but is unknown locally. is this right?: {:?}",
|
||||
id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[instrument]
|
||||
fn process_file_change(entry: &mut FileData, change: DriveFileMetadata) -> Result<()> {
|
||||
@@ -996,8 +1222,7 @@ fn process_file_change(entry: &mut FileData, change: DriveFileMetadata) -> Resul
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn remove_volatile_metadata(metadata: DriveFileMetadata) -> DriveFileMetadata {
|
||||
let mut metadata = metadata;
|
||||
fn remove_volatile_metadata(metadata: &mut DriveFileMetadata) {
|
||||
metadata.size = None;
|
||||
metadata.created_time = None;
|
||||
metadata.trashed_time = None;
|
||||
@@ -1008,11 +1233,9 @@ fn remove_volatile_metadata(metadata: DriveFileMetadata) -> DriveFileMetadata {
|
||||
metadata.viewed_by_me_time = None;
|
||||
metadata.explicitly_trashed = None;
|
||||
metadata.md5_checksum = None;
|
||||
metadata.parents = None;
|
||||
// metadata.parents = None;
|
||||
// parents have to be set differently: "The parents field is not directly writable in update requests. Use the addParents and removeParents parameters instead."
|
||||
metadata.kind = None;
|
||||
|
||||
metadata
|
||||
}
|
||||
|
||||
fn convert_mime_type_to_file_type(mime_type: &str) -> Result<FileType> {
|
||||
|
||||
@@ -21,6 +21,7 @@ pub enum ProviderResponse {
|
||||
Lookup(Option<FileMetadata>),
|
||||
ReadContent(Vec<u8>),
|
||||
ReadDir(ProviderReadDirResponse),
|
||||
Rename,
|
||||
WriteSize(u32),
|
||||
// Ok,
|
||||
Error(Error, c_int),
|
||||
@@ -36,6 +37,7 @@ pub enum ProviderRequest {
|
||||
SetAttr(ProviderSetAttrRequest),
|
||||
ReadContent(ProviderReadContentRequest),
|
||||
ReadDir(ProviderReadDirRequest),
|
||||
Rename(ProviderRenameRequest),
|
||||
WriteContent(ProviderWriteContentRequest),
|
||||
Unknown,
|
||||
}
|
||||
@@ -279,6 +281,34 @@ impl ProviderWriteContentRequest {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProviderRenameRequest {
|
||||
pub original_name: OsString,
|
||||
pub original_parent: DriveId,
|
||||
pub new_name: OsString,
|
||||
pub new_parent: DriveId,
|
||||
|
||||
pub response_sender: Sender<ProviderResponse>,
|
||||
}
|
||||
|
||||
impl ProviderRenameRequest {
|
||||
pub(crate) fn new(
|
||||
original_name: OsString,
|
||||
original_parent: DriveId,
|
||||
new_name: OsString,
|
||||
new_parent: DriveId,
|
||||
response_sender: Sender<ProviderResponse>,
|
||||
) -> Self {
|
||||
Self {
|
||||
original_name,
|
||||
original_parent,
|
||||
new_name,
|
||||
new_parent,
|
||||
response_sender,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
//region ProviderResponse structs
|
||||
|
||||
|
||||
Reference in New Issue
Block a user