Clean code and remove some TODOs
This commit is contained in:
parent
c63783d2fa
commit
28c7cf1f09
|
@ -8,7 +8,7 @@ use std::io::Read;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use crate::event::KeyModifier;
|
use crate::event::KeyModifier;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use log::{debug, info, warn, error};
|
use log::{error};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use regex::Regex;
|
||||||
use crate::system::SystemManager;
|
use crate::system::SystemManager;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use log::{debug, info, warn, error};
|
use log::{debug, warn};
|
||||||
use super::{Configs, ConfigSet};
|
use super::{Configs, ConfigSet};
|
||||||
use crate::matcher::Match;
|
use crate::matcher::Match;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@ use crate::event::KeyModifier::*;
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use log::{info};
|
use log::{info, error};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
const STATUS_ICON_BINARY : &'static [u8] = include_bytes!("../res/mac/icon.png");
|
const STATUS_ICON_BINARY : &'static [u8] = include_bytes!("../res/mac/icon.png");
|
||||||
|
|
||||||
|
@ -21,15 +22,15 @@ impl MacContext {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize the status icon path
|
// Initialize the status icon path
|
||||||
let data_dir = dirs::data_dir().expect("Can't obtain data_dir(), terminating.");
|
let espanso_dir = MacContext::get_data_dir();
|
||||||
let espanso_dir = data_dir.join("espanso");
|
|
||||||
let res = create_dir_all(&espanso_dir);
|
|
||||||
let status_icon_target = espanso_dir.join("icon.png");
|
let status_icon_target = espanso_dir.join("icon.png");
|
||||||
|
|
||||||
if status_icon_target.exists() {
|
if status_icon_target.exists() {
|
||||||
info!("Status icon already initialized, skipping.");
|
info!("Status icon already initialized, skipping.");
|
||||||
}else {
|
}else {
|
||||||
fs::write(&status_icon_target, STATUS_ICON_BINARY);
|
fs::write(&status_icon_target, STATUS_ICON_BINARY).unwrap_or_else(|e| {
|
||||||
|
error!("Error copying the Status Icon to the espanso data directory: {}", e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -45,6 +46,13 @@ impl MacContext {
|
||||||
|
|
||||||
context
|
context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_data_dir() -> PathBuf {
|
||||||
|
let data_dir = dirs::data_dir().expect("Can't obtain data_dir(), terminating.");
|
||||||
|
let espanso_dir = data_dir.join("espanso");
|
||||||
|
create_dir_all(&espanso_dir).expect("Error creating espanso data directory");
|
||||||
|
espanso_dir
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::Context for MacContext {
|
impl super::Context for MacContext {
|
||||||
|
|
|
@ -5,11 +5,10 @@ mod windows;
|
||||||
mod linux;
|
mod linux;
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod macos;
|
pub(crate) mod macos;
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
pub trait Context {
|
pub trait Context {
|
||||||
fn eventloop(&self);
|
fn eventloop(&self);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::matcher::{Match, MatchReceiver};
|
use crate::matcher::{Match, MatchReceiver};
|
||||||
use crate::keyboard::KeyboardSender;
|
use crate::keyboard::KeyboardManager;
|
||||||
use crate::config::ConfigManager;
|
use crate::config::ConfigManager;
|
||||||
use crate::config::BackendType;
|
use crate::config::BackendType;
|
||||||
use crate::clipboard::ClipboardManager;
|
use crate::clipboard::ClipboardManager;
|
||||||
|
@ -7,21 +7,22 @@ use log::{info};
|
||||||
use crate::ui::{UIManager, MenuItem, MenuItemType};
|
use crate::ui::{UIManager, MenuItem, MenuItemType};
|
||||||
use crate::event::{ActionEventReceiver, ActionEvent, ActionType};
|
use crate::event::{ActionEventReceiver, ActionEvent, ActionType};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
pub struct Engine<'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>,
|
pub struct Engine<'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>,
|
||||||
U: UIManager> {
|
U: UIManager> {
|
||||||
sender: S,
|
keyboard_manager: &'a S,
|
||||||
clipboard_manager: &'a C,
|
clipboard_manager: &'a C,
|
||||||
config_manager: &'a M,
|
config_manager: &'a M,
|
||||||
ui_manager: &'a U,
|
ui_manager: &'a U,
|
||||||
enabled: RefCell<bool>,
|
enabled: RefCell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager>
|
impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager>
|
||||||
Engine<'a, S, C, M, U> {
|
Engine<'a, S, C, M, U> {
|
||||||
pub fn new(sender: S, clipboard_manager: &'a C, config_manager: &'a M, ui_manager: &'a U) -> Engine<'a, S, C, M, U> {
|
pub fn new(keyboard_manager: &'a S, clipboard_manager: &'a C, config_manager: &'a M, ui_manager: &'a U) -> Engine<'a, S, C, M, U> {
|
||||||
let enabled = RefCell::new(true);
|
let enabled = RefCell::new(true);
|
||||||
Engine{sender, clipboard_manager, config_manager, ui_manager, enabled }
|
Engine{keyboard_manager, clipboard_manager, config_manager, ui_manager, enabled }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_menu(&self) -> Vec<MenuItem> {
|
fn build_menu(&self) -> Vec<MenuItem> {
|
||||||
|
@ -55,7 +56,7 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIMan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager>
|
impl <'a, S: KeyboardManager, C: ClipboardManager, M: ConfigManager<'a>, U: UIManager>
|
||||||
MatchReceiver for Engine<'a, S, C, M, U>{
|
MatchReceiver for Engine<'a, S, C, M, U>{
|
||||||
|
|
||||||
fn on_match(&self, m: &Match) {
|
fn on_match(&self, m: &Match) {
|
||||||
|
@ -65,7 +66,7 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIMan
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sender.delete_string(m.trigger.len() as i32);
|
self.keyboard_manager.delete_string(m.trigger.len() as i32);
|
||||||
|
|
||||||
match config.backend {
|
match config.backend {
|
||||||
BackendType::Inject => {
|
BackendType::Inject => {
|
||||||
|
@ -73,23 +74,23 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIMan
|
||||||
// while on windows and macos, we need to emulate a Enter key press.
|
// while on windows and macos, we need to emulate a Enter key press.
|
||||||
|
|
||||||
if cfg!(target_os = "linux") {
|
if cfg!(target_os = "linux") {
|
||||||
self.sender.send_string(m.replace.as_str());
|
self.keyboard_manager.send_string(m.replace.as_str());
|
||||||
}else{
|
}else{
|
||||||
// To handle newlines, substitute each "\n" char with an Enter key press.
|
// To handle newlines, substitute each "\n" char with an Enter key press.
|
||||||
let splits = m.replace.lines();
|
let splits = m.replace.lines();
|
||||||
|
|
||||||
for (i, split) in splits.enumerate() {
|
for (i, split) in splits.enumerate() {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
self.sender.send_enter();
|
self.keyboard_manager.send_enter();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.sender.send_string(split);
|
self.keyboard_manager.send_string(split);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BackendType::Clipboard => {
|
BackendType::Clipboard => {
|
||||||
self.clipboard_manager.set_clipboard(m.replace.as_str());
|
self.clipboard_manager.set_clipboard(m.replace.as_str());
|
||||||
self.sender.trigger_paste();
|
self.keyboard_manager.trigger_paste();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +111,7 @@ impl <'a, S: KeyboardSender, C: ClipboardManager, M: ConfigManager<'a>, U: UIMan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, S: KeyboardSender, C: ClipboardManager,
|
impl <'a, S: KeyboardManager, C: ClipboardManager,
|
||||||
M: ConfigManager<'a>, U: UIManager> ActionEventReceiver for Engine<'a, S, C, M, U>{
|
M: ConfigManager<'a>, U: UIManager> ActionEventReceiver for Engine<'a, S, C, M, U>{
|
||||||
|
|
||||||
fn on_action_event(&self, e: ActionEvent) {
|
fn on_action_event(&self, e: ActionEvent) {
|
||||||
|
@ -119,7 +120,13 @@ impl <'a, S: KeyboardSender, C: ClipboardManager,
|
||||||
self.ui_manager.show_menu(self.build_menu());
|
self.ui_manager.show_menu(self.build_menu());
|
||||||
},
|
},
|
||||||
ActionEvent::ContextMenuClick(id) => {
|
ActionEvent::ContextMenuClick(id) => {
|
||||||
println!("{:?}",id);
|
match id {
|
||||||
|
ActionType::Exit => {
|
||||||
|
info!("Terminating expanso from the context menu");
|
||||||
|
exit(0);
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,5 +59,5 @@ pub trait KeyEventReceiver {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ActionEventReceiver {
|
pub trait ActionEventReceiver {
|
||||||
fn on_action_event(&self, e: ActionEvent); // TODO: Action event
|
fn on_action_event(&self, e: ActionEvent);
|
||||||
}
|
}
|
|
@ -32,10 +32,10 @@ impl Drop for LinuxKeyboardInterceptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LinuxKeyboardSender {
|
pub struct LinuxKeyboardManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::KeyboardSender for LinuxKeyboardSender {
|
impl super::KeyboardManager for LinuxKeyboardManager {
|
||||||
fn send_string(&self, s: &str) {
|
fn send_string(&self, s: &str) {
|
||||||
let res = CString::new(s);
|
let res = CString::new(s);
|
||||||
match res {
|
match res {
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::os::raw::{c_char, c_void};
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use crate::bridge::macos::*;
|
use crate::bridge::macos::*;
|
||||||
|
|
||||||
pub struct MacKeyboardSender {
|
pub struct MacKeyboardManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::KeyboardSender for MacKeyboardSender {
|
impl super::KeyboardManager for MacKeyboardManager {
|
||||||
fn send_string(&self, s: &str) {
|
fn send_string(&self, s: &str) {
|
||||||
let res = CString::new(s);
|
let res = CString::new(s);
|
||||||
match res {
|
match res {
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod linux;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod macos;
|
mod macos;
|
||||||
|
|
||||||
pub trait KeyboardSender { // TODO: rename KeyboardManager
|
pub trait KeyboardManager {
|
||||||
fn send_string(&self, s: &str);
|
fn send_string(&self, s: &str);
|
||||||
fn send_enter(&self);
|
fn send_enter(&self);
|
||||||
fn trigger_paste(&self);
|
fn trigger_paste(&self);
|
||||||
|
@ -16,18 +16,18 @@ pub trait KeyboardSender { // TODO: rename KeyboardManager
|
||||||
|
|
||||||
// WINDOWS IMPLEMENTATION
|
// WINDOWS IMPLEMENTATION
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub fn get_sender() -> impl KeyboardSender {
|
pub fn get_manager() -> impl KeyboardManager {
|
||||||
windows::WindowsKeyboardSender{}
|
windows::WindowsKeyboardManager{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LINUX IMPLEMENTATION
|
// LINUX IMPLEMENTATION
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn get_sender() -> impl KeyboardSender {
|
pub fn get_manager() -> impl KeyboardManager {
|
||||||
linux::LinuxKeyboardSender{}
|
linux::LinuxKeyboardManager{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MAC IMPLEMENTATION
|
// MAC IMPLEMENTATION
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
pub fn get_sender() -> impl KeyboardSender {
|
pub fn get_manager() -> impl KeyboardManager {
|
||||||
macos::MacKeyboardSender{}
|
macos::MacKeyboardManager{}
|
||||||
}
|
}
|
|
@ -3,10 +3,10 @@ use std::os::raw::{c_void};
|
||||||
use widestring::{U16CString};
|
use widestring::{U16CString};
|
||||||
use crate::bridge::windows::*;
|
use crate::bridge::windows::*;
|
||||||
|
|
||||||
pub struct WindowsKeyboardSender {
|
pub struct WindowsKeyboardManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl super::KeyboardSender for WindowsKeyboardSender {
|
impl super::KeyboardManager for WindowsKeyboardManager {
|
||||||
fn send_string(&self, s: &str) {
|
fn send_string(&self, s: &str) {
|
||||||
let res = U16CString::from_str(s);
|
let res = U16CString::from_str(s);
|
||||||
match res {
|
match res {
|
||||||
|
|
|
@ -4,7 +4,6 @@ use crate::engine::Engine;
|
||||||
use crate::config::ConfigSet;
|
use crate::config::ConfigSet;
|
||||||
use crate::config::runtime::RuntimeConfigManager;
|
use crate::config::runtime::RuntimeConfigManager;
|
||||||
use crate::ui::UIManager;
|
use crate::ui::UIManager;
|
||||||
use crate::context::Context;
|
|
||||||
use crate::event::*;
|
use crate::event::*;
|
||||||
use crate::event::manager::{EventManager, DefaultEventManager};
|
use crate::event::manager::{EventManager, DefaultEventManager};
|
||||||
use std::{thread};
|
use std::{thread};
|
||||||
|
@ -107,10 +106,9 @@ fn espanso_background(receive_channel: Receiver<Event>, config_set: ConfigSet) {
|
||||||
|
|
||||||
let clipboard_manager = clipboard::get_manager();
|
let clipboard_manager = clipboard::get_manager();
|
||||||
|
|
||||||
let sender = keyboard::get_sender(); // TODO: rename manager
|
let manager = keyboard::get_manager();
|
||||||
|
|
||||||
// TODO: change sender from move to reference
|
let engine = Engine::new(&manager,
|
||||||
let engine = Engine::new(sender,
|
|
||||||
&clipboard_manager,
|
&clipboard_manager,
|
||||||
&config_manager,
|
&config_manager,
|
||||||
&ui_manager
|
&ui_manager
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::sync::mpsc::Receiver;
|
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use crate::event::{KeyEvent, KeyModifier};
|
use crate::event::{KeyEvent, KeyModifier};
|
||||||
use crate::event::KeyEventReceiver;
|
use crate::event::KeyEventReceiver;
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use std::fs::create_dir_all;
|
|
||||||
use std::{fs, io};
|
use std::{fs, io};
|
||||||
use std::io::{Cursor};
|
use std::io::{Cursor};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use log::{info, debug};
|
use log::{info, warn, debug};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use crate::ui::{MenuItem, MenuItemType};
|
use crate::ui::{MenuItem, MenuItemType};
|
||||||
|
use crate::context::macos::MacContext;
|
||||||
use crate::bridge::macos::{MacMenuItem, show_context_menu};
|
use crate::bridge::macos::{MacMenuItem, show_context_menu};
|
||||||
use std::os::raw::c_char;
|
use std::os::raw::c_char;
|
||||||
|
|
||||||
|
@ -25,6 +25,10 @@ impl super::UIManager for MacUIManager {
|
||||||
let res = Command::new(executable_path)
|
let res = Command::new(executable_path)
|
||||||
.args(&["espanso", message, &DEFAULT_NOTIFICATION_DELAY.to_string()])
|
.args(&["espanso", message, &DEFAULT_NOTIFICATION_DELAY.to_string()])
|
||||||
.spawn();
|
.spawn();
|
||||||
|
|
||||||
|
if let Err(e) = res {
|
||||||
|
warn!("Error while dispatching Notify Helper {}", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_menu(&self, menu: Vec<MenuItem>) {
|
fn show_menu(&self, menu: Vec<MenuItem>) {
|
||||||
|
@ -65,11 +69,7 @@ impl MacUIManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_notify_helper() -> PathBuf {
|
fn initialize_notify_helper() -> PathBuf {
|
||||||
let data_dir = dirs::data_dir().expect("Can't obtain data_dir(), terminating.");
|
let espanso_dir = MacContext::get_data_dir();
|
||||||
|
|
||||||
let espanso_dir = data_dir.join("espanso");
|
|
||||||
|
|
||||||
let res = create_dir_all(&espanso_dir);
|
|
||||||
|
|
||||||
info!("Initializing EspansoNotifyHelper in {}", espanso_dir.as_path().display());
|
info!("Initializing EspansoNotifyHelper in {}", espanso_dir.as_path().display());
|
||||||
|
|
||||||
|
@ -78,42 +78,40 @@ impl MacUIManager {
|
||||||
if espanso_target.exists() {
|
if espanso_target.exists() {
|
||||||
info!("EspansoNotifyHelper already initialized, skipping.");
|
info!("EspansoNotifyHelper already initialized, skipping.");
|
||||||
}else{
|
}else{
|
||||||
if let Ok(_) = res {
|
// Extract zip file
|
||||||
// Extract zip file
|
let reader = Cursor::new(NOTIFY_HELPER_BINARY);
|
||||||
let reader = Cursor::new(NOTIFY_HELPER_BINARY);
|
|
||||||
|
|
||||||
let mut archive = zip::ZipArchive::new(reader).unwrap();
|
let mut archive = zip::ZipArchive::new(reader).unwrap();
|
||||||
|
|
||||||
for i in 0..archive.len() {
|
for i in 0..archive.len() {
|
||||||
let mut file = archive.by_index(i).unwrap();
|
let mut file = archive.by_index(i).unwrap();
|
||||||
let outpath = espanso_dir.join(file.sanitized_name());
|
let outpath = espanso_dir.join(file.sanitized_name());
|
||||||
|
|
||||||
{
|
{
|
||||||
let comment = file.comment();
|
let comment = file.comment();
|
||||||
if !comment.is_empty() {
|
if !comment.is_empty() {
|
||||||
debug!("File {} comment: {}", i, comment);
|
debug!("File {} comment: {}", i, comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (&*file.name()).ends_with('/') {
|
||||||
|
debug!("File {} extracted to \"{}\"", i, outpath.as_path().display());
|
||||||
|
fs::create_dir_all(&outpath).unwrap();
|
||||||
|
} else {
|
||||||
|
debug!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size());
|
||||||
|
if let Some(p) = outpath.parent() {
|
||||||
|
if !p.exists() {
|
||||||
|
fs::create_dir_all(&p).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let mut outfile = fs::File::create(&outpath).unwrap();
|
||||||
|
io::copy(&mut file, &mut outfile).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
if (&*file.name()).ends_with('/') {
|
use std::os::unix::fs::PermissionsExt;
|
||||||
debug!("File {} extracted to \"{}\"", i, outpath.as_path().display());
|
|
||||||
fs::create_dir_all(&outpath).unwrap();
|
|
||||||
} else {
|
|
||||||
debug!("File {} extracted to \"{}\" ({} bytes)", i, outpath.as_path().display(), file.size());
|
|
||||||
if let Some(p) = outpath.parent() {
|
|
||||||
if !p.exists() {
|
|
||||||
fs::create_dir_all(&p).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let mut outfile = fs::File::create(&outpath).unwrap();
|
|
||||||
io::copy(&mut file, &mut outfile).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
if let Some(mode) = file.unix_mode() {
|
||||||
|
fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap();
|
||||||
if let Some(mode) = file.unix_mode() {
|
|
||||||
fs::set_permissions(&outpath, fs::Permissions::from_mode(mode)).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user