mirror of
https://github.com/veeso/termscp.git
synced 2025-12-07 09:36:00 -08:00
Utils into multiple files
This commit is contained in:
@@ -30,7 +30,7 @@ extern crate regex;
|
|||||||
|
|
||||||
use super::{FileTransfer, FileTransferError, FileTransferErrorType};
|
use super::{FileTransfer, FileTransferError, FileTransferErrorType};
|
||||||
use crate::fs::{FsDirectory, FsEntry, FsFile};
|
use crate::fs::{FsDirectory, FsEntry, FsFile};
|
||||||
use crate::utils::lstime_to_systime;
|
use crate::utils::parser::parse_lstime;
|
||||||
|
|
||||||
// Includes
|
// Includes
|
||||||
use ftp4::native_tls::TlsConnector;
|
use ftp4::native_tls::TlsConnector;
|
||||||
@@ -142,7 +142,7 @@ impl FtpFileTransfer {
|
|||||||
(owner_pex, group_pex, others_pex)
|
(owner_pex, group_pex, others_pex)
|
||||||
};
|
};
|
||||||
// Parse mtime and convert to SystemTime
|
// Parse mtime and convert to SystemTime
|
||||||
let mtime: SystemTime = match lstime_to_systime(
|
let mtime: SystemTime = match parse_lstime(
|
||||||
metadata.get(7).unwrap().as_str(),
|
metadata.get(7).unwrap().as_str(),
|
||||||
"%b %d %Y",
|
"%b %d %Y",
|
||||||
"%b %d %H:%M",
|
"%b %d %H:%M",
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ extern crate ssh2;
|
|||||||
// Locals
|
// Locals
|
||||||
use super::{FileTransfer, FileTransferError, FileTransferErrorType};
|
use super::{FileTransfer, FileTransferError, FileTransferErrorType};
|
||||||
use crate::fs::{FsDirectory, FsEntry, FsFile};
|
use crate::fs::{FsDirectory, FsEntry, FsFile};
|
||||||
use crate::utils::lstime_to_systime;
|
use crate::utils::parser::parse_lstime;
|
||||||
|
|
||||||
// Includes
|
// Includes
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
@@ -151,7 +151,7 @@ impl ScpFileTransfer {
|
|||||||
(owner_pex, group_pex, others_pex)
|
(owner_pex, group_pex, others_pex)
|
||||||
};
|
};
|
||||||
// Parse mtime and convert to SystemTime
|
// Parse mtime and convert to SystemTime
|
||||||
let mtime: SystemTime = match lstime_to_systime(
|
let mtime: SystemTime = match parse_lstime(
|
||||||
metadata.get(7).unwrap().as_str(),
|
metadata.get(7).unwrap().as_str(),
|
||||||
"%b %d %Y",
|
"%b %d %Y",
|
||||||
"%b %d %H:%M",
|
"%b %d %H:%M",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ extern crate bytesize;
|
|||||||
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
|
#[cfg(any(target_os = "unix", target_os = "macos", target_os = "linux"))]
|
||||||
extern crate users;
|
extern crate users;
|
||||||
|
|
||||||
use crate::utils::{fmt_pex, time_to_str};
|
use crate::utils::fmt::{fmt_pex, fmt_time};
|
||||||
|
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -271,7 +271,7 @@ impl std::fmt::Display for FsEntry {
|
|||||||
// Get byte size
|
// Get byte size
|
||||||
let size: ByteSize = ByteSize(self.get_size() as u64);
|
let size: ByteSize = ByteSize(self.get_size() as u64);
|
||||||
// Get date
|
// Get date
|
||||||
let datetime: String = time_to_str(self.get_last_change_time(), "%b %d %Y %H:%M");
|
let datetime: String = fmt_time(self.get_last_change_time(), "%b %d %Y %H:%M");
|
||||||
// Set file name (or elide if too long)
|
// Set file name (or elide if too long)
|
||||||
let name: String = self.get_name();
|
let name: String = self.get_name();
|
||||||
let name: String = match name.len() >= 24 {
|
let name: String = match name.len() >= 24 {
|
||||||
@@ -467,7 +467,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"bar.txt \t-rw-r--r-- \troot \t8.2 KB \t{}",
|
"bar.txt \t-rw-r--r-- \troot \t8.2 KB \t{}",
|
||||||
time_to_str(t, "%b %d %Y %H:%M")
|
fmt_time(t, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -475,7 +475,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"bar.txt \t-rw-r--r-- \t0 \t8.2 KB \t{}",
|
"bar.txt \t-rw-r--r-- \t0 \t8.2 KB \t{}",
|
||||||
time_to_str(t, "%b %d %Y %H:%M")
|
fmt_time(t, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// Elide name
|
// Elide name
|
||||||
@@ -498,7 +498,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"piroparoporoperoperu... \t-rw-r--r-- \troot \t8.2 KB \t{}",
|
"piroparoporoperoperu... \t-rw-r--r-- \troot \t8.2 KB \t{}",
|
||||||
time_to_str(t, "%b %d %Y %H:%M")
|
fmt_time(t, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -506,7 +506,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"piroparoporoperoperu... \t-rw-r--r-- \t0 \t8.2 KB \t{}",
|
"piroparoporoperoperu... \t-rw-r--r-- \t0 \t8.2 KB \t{}",
|
||||||
time_to_str(t, "%b %d %Y %H:%M")
|
fmt_time(t, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// No pex
|
// No pex
|
||||||
@@ -529,7 +529,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"bar.txt \t-????????? \troot \t8.2 KB \t{}",
|
"bar.txt \t-????????? \troot \t8.2 KB \t{}",
|
||||||
time_to_str(t, "%b %d %Y %H:%M")
|
fmt_time(t, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -537,7 +537,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"bar.txt \t-????????? \t0 \t8.2 KB \t{}",
|
"bar.txt \t-????????? \t0 \t8.2 KB \t{}",
|
||||||
time_to_str(t, "%b %d %Y %H:%M")
|
fmt_time(t, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// No user
|
// No user
|
||||||
@@ -560,7 +560,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"bar.txt \t-????????? \t0 \t8.2 KB \t{}",
|
"bar.txt \t-????????? \t0 \t8.2 KB \t{}",
|
||||||
time_to_str(t, "%b %d %Y %H:%M")
|
fmt_time(t, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -568,7 +568,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"bar.txt \t-????????? \t0 \t8.2 KB \t{}",
|
"bar.txt \t-????????? \t0 \t8.2 KB \t{}",
|
||||||
time_to_str(t, "%b %d %Y %H:%M")
|
fmt_time(t, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -593,7 +593,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"projects \tdrwxr-xr-x \troot \t4.1 KB \t{}",
|
"projects \tdrwxr-xr-x \troot \t4.1 KB \t{}",
|
||||||
time_to_str(t_now, "%b %d %Y %H:%M")
|
fmt_time(t_now, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -601,7 +601,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"projects \tdrwxr-xr-x \t0 \t4.1 KB \t{}",
|
"projects \tdrwxr-xr-x \t0 \t4.1 KB \t{}",
|
||||||
time_to_str(t_now, "%b %d %Y %H:%M")
|
fmt_time(t_now, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
// No pex, no user
|
// No pex, no user
|
||||||
@@ -622,7 +622,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"projects \td????????? \t0 \t4.1 KB \t{}",
|
"projects \td????????? \t0 \t4.1 KB \t{}",
|
||||||
time_to_str(t_now, "%b %d %Y %H:%M")
|
fmt_time(t_now, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -630,7 +630,7 @@ mod tests {
|
|||||||
format!("{}", entry),
|
format!("{}", entry),
|
||||||
format!(
|
format!(
|
||||||
"projects \td????????? \t0 \t4.1 KB \t{}",
|
"projects \td????????? \t0 \t4.1 KB \t{}",
|
||||||
time_to_str(t_now, "%b %d %Y %H:%M")
|
fmt_time(t_now, "%b %d %Y %H:%M")
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ fn main() {
|
|||||||
let extra_args: Vec<String> = matches.free;
|
let extra_args: Vec<String> = matches.free;
|
||||||
if let Some(remote) = extra_args.get(0) {
|
if let Some(remote) = extra_args.get(0) {
|
||||||
// Parse address
|
// Parse address
|
||||||
match utils::parse_remote_opt(remote) {
|
match utils::parser::parse_remote_opt(remote) {
|
||||||
Ok((addr, portn, proto, user)) => {
|
Ok((addr, portn, proto, user)) => {
|
||||||
// Set params
|
// Set params
|
||||||
address = Some(addr);
|
address = Some(addr);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ extern crate rand;
|
|||||||
use crate::bookmarks::serializer::BookmarkSerializer;
|
use crate::bookmarks::serializer::BookmarkSerializer;
|
||||||
use crate::bookmarks::{Bookmark, SerializerError, SerializerErrorKind, UserHosts};
|
use crate::bookmarks::{Bookmark, SerializerError, SerializerErrorKind, UserHosts};
|
||||||
use crate::filetransfer::FileTransferProtocol;
|
use crate::filetransfer::FileTransferProtocol;
|
||||||
use crate::utils::time_to_str;
|
use crate::utils::fmt::fmt_time;
|
||||||
// Ext
|
// Ext
|
||||||
use magic_crypt::MagicCryptTrait;
|
use magic_crypt::MagicCryptTrait;
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
@@ -220,7 +220,7 @@ impl BookmarksClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let name: String = time_to_str(SystemTime::now(), "ISO%Y%m%dT%H%M%S");
|
let name: String = fmt_time(SystemTime::now(), "ISO%Y%m%dT%H%M%S");
|
||||||
self.hosts.recents.insert(name, host);
|
self.hosts.recents.insert(name, host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use super::{
|
|||||||
AuthActivity, Context, DialogYesNoOption, FileTransferProtocol, InputField, InputForm,
|
AuthActivity, Context, DialogYesNoOption, FileTransferProtocol, InputField, InputForm,
|
||||||
InputMode, PopupType,
|
InputMode, PopupType,
|
||||||
};
|
};
|
||||||
use crate::utils::align_text_center;
|
use crate::utils::fmt::align_text_center;
|
||||||
|
|
||||||
use tui::{
|
use tui::{
|
||||||
layout::{Constraint, Corner, Direction, Layout, Rect},
|
layout::{Constraint, Corner, Direction, Layout, Rect},
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use super::{
|
|||||||
Context, DialogYesNoOption, FileExplorerTab, FileTransferActivity, FsEntry, InputField,
|
Context, DialogYesNoOption, FileExplorerTab, FileTransferActivity, FsEntry, InputField,
|
||||||
InputMode, LogLevel, LogRecord, PopupType,
|
InputMode, LogLevel, LogRecord, PopupType,
|
||||||
};
|
};
|
||||||
use crate::utils::{align_text_center, time_to_str};
|
use crate::utils::fmt::{align_text_center, fmt_time};
|
||||||
|
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -484,10 +484,10 @@ impl FileTransferActivity {
|
|||||||
// Get name and path
|
// Get name and path
|
||||||
let abs_path: PathBuf = fsentry.get_abs_path();
|
let abs_path: PathBuf = fsentry.get_abs_path();
|
||||||
let name: String = fsentry.get_name();
|
let name: String = fsentry.get_name();
|
||||||
let ctime: String = time_to_str(fsentry.get_creation_time(), "%b %d %Y %H:%M:%S");
|
let ctime: String = fmt_time(fsentry.get_creation_time(), "%b %d %Y %H:%M:%S");
|
||||||
let atime: String =
|
let atime: String =
|
||||||
time_to_str(fsentry.get_last_access_time(), "%b %d %Y %H:%M:%S");
|
fmt_time(fsentry.get_last_access_time(), "%b %d %Y %H:%M:%S");
|
||||||
let mtime: String = time_to_str(fsentry.get_creation_time(), "%b %d %Y %H:%M:%S");
|
let mtime: String = fmt_time(fsentry.get_creation_time(), "%b %d %Y %H:%M:%S");
|
||||||
let (bsize, size): (ByteSize, usize) =
|
let (bsize, size): (ByteSize, usize) =
|
||||||
(ByteSize(fsentry.get_size() as u64), fsentry.get_size());
|
(ByteSize(fsentry.get_size() as u64), fsentry.get_size());
|
||||||
let user: Option<u32> = fsentry.get_user();
|
let user: Option<u32> = fsentry.get_user();
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ extern crate tempfile;
|
|||||||
// Locals
|
// Locals
|
||||||
use super::{FileTransferActivity, InputMode, LogLevel, PopupType};
|
use super::{FileTransferActivity, InputMode, LogLevel, PopupType};
|
||||||
use crate::fs::{FsEntry, FsFile};
|
use crate::fs::{FsEntry, FsFile};
|
||||||
use crate::utils::{fmt_millis, hash_sha256_file};
|
use crate::utils::fmt::fmt_millis;
|
||||||
|
use crate::utils::hash::hash_sha256_file;
|
||||||
|
|
||||||
// Ext
|
// Ext
|
||||||
use bytesize::ByteSize;
|
use bytesize::ByteSize;
|
||||||
|
|||||||
172
src/utils/fmt.rs
Normal file
172
src/utils/fmt.rs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
//! ## Fmt
|
||||||
|
//!
|
||||||
|
//! `fmt` is the module which provides utilities for formatting
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Christian Visintin - christian.visintin1997@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of "TermSCP"
|
||||||
|
*
|
||||||
|
* TermSCP is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* TermSCP is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with TermSCP. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern crate chrono;
|
||||||
|
extern crate textwrap;
|
||||||
|
|
||||||
|
use chrono::prelude::*;
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
/// ### fmt_pex
|
||||||
|
///
|
||||||
|
/// Convert 3 bytes of permissions value into ls notation (e.g. rwx-wx--x)
|
||||||
|
pub fn fmt_pex(owner: u8, group: u8, others: u8) -> String {
|
||||||
|
let mut mode: String = String::with_capacity(9);
|
||||||
|
let read: u8 = (owner >> 2) & 0x1;
|
||||||
|
let write: u8 = (owner >> 1) & 0x1;
|
||||||
|
let exec: u8 = owner & 0x1;
|
||||||
|
mode.push_str(match read {
|
||||||
|
1 => "r",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
mode.push_str(match write {
|
||||||
|
1 => "w",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
mode.push_str(match exec {
|
||||||
|
1 => "x",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
let read: u8 = (group >> 2) & 0x1;
|
||||||
|
let write: u8 = (group >> 1) & 0x1;
|
||||||
|
let exec: u8 = group & 0x1;
|
||||||
|
mode.push_str(match read {
|
||||||
|
1 => "r",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
mode.push_str(match write {
|
||||||
|
1 => "w",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
mode.push_str(match exec {
|
||||||
|
1 => "x",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
let read: u8 = (others >> 2) & 0x1;
|
||||||
|
let write: u8 = (others >> 1) & 0x1;
|
||||||
|
let exec: u8 = others & 0x1;
|
||||||
|
mode.push_str(match read {
|
||||||
|
1 => "r",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
mode.push_str(match write {
|
||||||
|
1 => "w",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
mode.push_str(match exec {
|
||||||
|
1 => "x",
|
||||||
|
_ => "-",
|
||||||
|
});
|
||||||
|
mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ### instant_to_str
|
||||||
|
///
|
||||||
|
/// Format a `Instant` into a time string
|
||||||
|
pub fn fmt_time(time: SystemTime, fmt: &str) -> String {
|
||||||
|
let datetime: DateTime<Local> = time.into();
|
||||||
|
format!("{}", datetime.format(fmt))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ### fmt_millis
|
||||||
|
///
|
||||||
|
/// Format duration as {secs}.{millis}
|
||||||
|
pub fn fmt_millis(duration: Duration) -> String {
|
||||||
|
let seconds: u128 = duration.as_millis() / 1000;
|
||||||
|
let millis: u128 = duration.as_millis() % 1000;
|
||||||
|
format!("{}.{:0width$}", seconds, millis, width = 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// align_text_center
|
||||||
|
///
|
||||||
|
/// Align text to center for a given width
|
||||||
|
pub fn align_text_center(text: &str, width: u16) -> String {
|
||||||
|
let indent_size: usize = match (width as usize) >= text.len() {
|
||||||
|
// NOTE: The check prevents underflow
|
||||||
|
true => (width as usize - text.len()) / 2,
|
||||||
|
false => 0,
|
||||||
|
};
|
||||||
|
textwrap::indent(
|
||||||
|
text,
|
||||||
|
(0..indent_size).map(|_| " ").collect::<String>().as_str(),
|
||||||
|
)
|
||||||
|
.trim_end()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_utils_fmt_pex() {
|
||||||
|
assert_eq!(fmt_pex(7, 7, 7), String::from("rwxrwxrwx"));
|
||||||
|
assert_eq!(fmt_pex(7, 5, 5), String::from("rwxr-xr-x"));
|
||||||
|
assert_eq!(fmt_pex(6, 6, 6), String::from("rw-rw-rw-"));
|
||||||
|
assert_eq!(fmt_pex(6, 4, 4), String::from("rw-r--r--"));
|
||||||
|
assert_eq!(fmt_pex(6, 0, 0), String::from("rw-------"));
|
||||||
|
assert_eq!(fmt_pex(0, 0, 0), String::from("---------"));
|
||||||
|
assert_eq!(fmt_pex(4, 4, 4), String::from("r--r--r--"));
|
||||||
|
assert_eq!(fmt_pex(1, 2, 1), String::from("--x-w---x"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_utils_fmt_time() {
|
||||||
|
let system_time: SystemTime = SystemTime::from(SystemTime::UNIX_EPOCH);
|
||||||
|
assert_eq!(
|
||||||
|
fmt_time(system_time, "%Y-%m-%d"),
|
||||||
|
String::from("1970-01-01")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_utils_align_text_center() {
|
||||||
|
assert_eq!(
|
||||||
|
align_text_center("hello world!", 24),
|
||||||
|
String::from(" hello world!")
|
||||||
|
);
|
||||||
|
// Bad case
|
||||||
|
assert_eq!(
|
||||||
|
align_text_center("hello world!", 8),
|
||||||
|
String::from("hello world!")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_utils_fmt_millis() {
|
||||||
|
assert_eq!(
|
||||||
|
fmt_millis(Duration::from_millis(2048)),
|
||||||
|
String::from("2.048")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fmt_millis(Duration::from_millis(8192)),
|
||||||
|
String::from("8.192")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
fmt_millis(Duration::from_millis(18192)),
|
||||||
|
String::from("18.192")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/utils/hash.rs
Normal file
75
src/utils/hash.rs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
//! ## Hash
|
||||||
|
//!
|
||||||
|
//! `hash` is the module which provides utilities for calculating digests
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Christian Visintin - christian.visintin1997@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of "TermSCP"
|
||||||
|
*
|
||||||
|
* TermSCP is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* TermSCP is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with TermSCP. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern crate data_encoding;
|
||||||
|
extern crate ring;
|
||||||
|
|
||||||
|
use data_encoding::HEXLOWER;
|
||||||
|
use ring::digest::{Context, Digest, SHA256};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
/// ### hash_sha256_file
|
||||||
|
///
|
||||||
|
/// Get SHA256 of provided path
|
||||||
|
pub fn hash_sha256_file(file: &Path) -> Result<String, std::io::Error> {
|
||||||
|
// Open file
|
||||||
|
let mut reader: File = File::open(file)?;
|
||||||
|
let mut context = Context::new(&SHA256);
|
||||||
|
let mut buffer = [0; 8192];
|
||||||
|
loop {
|
||||||
|
let count = reader.read(&mut buffer)?;
|
||||||
|
if count == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
context.update(&buffer[..count]);
|
||||||
|
}
|
||||||
|
// Finish context
|
||||||
|
let digest: Digest = context.finish();
|
||||||
|
Ok(HEXLOWER.encode(digest.as_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_utils_hash_sha256() {
|
||||||
|
let tmp: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
|
||||||
|
// Write
|
||||||
|
let mut fhnd: File = File::create(tmp.path()).unwrap();
|
||||||
|
assert!(fhnd.write_all(b"Hello world!\n").is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
*hash_sha256_file(tmp.path()).ok().as_ref().unwrap(),
|
||||||
|
String::from("0ba904eae8773b70c75333db4de2f3ac45a8ad4ddba1b242f0b3cfc199391dd8")
|
||||||
|
);
|
||||||
|
// Bad file
|
||||||
|
assert!(hash_sha256_file(Path::new("/tmp/oiojjt5ig/aiehgoiwg")).is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/utils/mod.rs
Normal file
29
src/utils/mod.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//! ## Utils
|
||||||
|
//!
|
||||||
|
//! `utils` is the module which provides utilities of different kind
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Christian Visintin - christian.visintin1997@gmail.com
|
||||||
|
*
|
||||||
|
* This file is part of "TermSCP"
|
||||||
|
*
|
||||||
|
* TermSCP is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* TermSCP is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with TermSCP. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// modules
|
||||||
|
pub mod fmt;
|
||||||
|
pub mod hash;
|
||||||
|
pub mod parser;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
//! ## Utils
|
//! ## Parser
|
||||||
//!
|
//!
|
||||||
//! `utils` is the module which provides utilities of different kind
|
//! `parser` is the module which provides utilities for parsing different kind of stuff
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
@@ -25,20 +25,12 @@
|
|||||||
|
|
||||||
// Dependencies
|
// Dependencies
|
||||||
extern crate chrono;
|
extern crate chrono;
|
||||||
extern crate data_encoding;
|
|
||||||
extern crate ring;
|
|
||||||
extern crate textwrap;
|
|
||||||
extern crate whoami;
|
extern crate whoami;
|
||||||
|
|
||||||
use crate::filetransfer::FileTransferProtocol;
|
use crate::filetransfer::FileTransferProtocol;
|
||||||
|
|
||||||
use chrono::format::ParseError;
|
use chrono::format::ParseError;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use data_encoding::HEXLOWER;
|
|
||||||
use ring::digest::{Context, Digest, SHA256};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
/// ### parse_remote_opt
|
/// ### parse_remote_opt
|
||||||
@@ -152,87 +144,13 @@ pub fn parse_remote_opt(
|
|||||||
Ok((address, port, protocol, username))
|
Ok((address, port, protocol, username))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ### fmt_pex
|
/// ### parse_lstime
|
||||||
///
|
|
||||||
/// Convert 3 bytes of permissions value into ls notation (e.g. rwx-wx--x)
|
|
||||||
pub fn fmt_pex(owner: u8, group: u8, others: u8) -> String {
|
|
||||||
let mut mode: String = String::with_capacity(9);
|
|
||||||
let read: u8 = (owner >> 2) & 0x1;
|
|
||||||
let write: u8 = (owner >> 1) & 0x1;
|
|
||||||
let exec: u8 = owner & 0x1;
|
|
||||||
mode.push_str(match read {
|
|
||||||
1 => "r",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
mode.push_str(match write {
|
|
||||||
1 => "w",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
mode.push_str(match exec {
|
|
||||||
1 => "x",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
let read: u8 = (group >> 2) & 0x1;
|
|
||||||
let write: u8 = (group >> 1) & 0x1;
|
|
||||||
let exec: u8 = group & 0x1;
|
|
||||||
mode.push_str(match read {
|
|
||||||
1 => "r",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
mode.push_str(match write {
|
|
||||||
1 => "w",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
mode.push_str(match exec {
|
|
||||||
1 => "x",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
let read: u8 = (others >> 2) & 0x1;
|
|
||||||
let write: u8 = (others >> 1) & 0x1;
|
|
||||||
let exec: u8 = others & 0x1;
|
|
||||||
mode.push_str(match read {
|
|
||||||
1 => "r",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
mode.push_str(match write {
|
|
||||||
1 => "w",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
mode.push_str(match exec {
|
|
||||||
1 => "x",
|
|
||||||
_ => "-",
|
|
||||||
});
|
|
||||||
mode
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ### instant_to_str
|
|
||||||
///
|
|
||||||
/// Format a `Instant` into a time string
|
|
||||||
pub fn time_to_str(time: SystemTime, fmt: &str) -> String {
|
|
||||||
let datetime: DateTime<Local> = time.into();
|
|
||||||
format!("{}", datetime.format(fmt))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ### fmt_millis
|
|
||||||
///
|
|
||||||
/// Format duration as {secs}.{millis}
|
|
||||||
pub fn fmt_millis(duration: Duration) -> String {
|
|
||||||
let seconds: u128 = duration.as_millis() / 1000;
|
|
||||||
let millis: u128 = duration.as_millis() % 1000;
|
|
||||||
format!("{}.{:0width$}", seconds, millis, width = 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ### lstime_to_systime
|
|
||||||
///
|
///
|
||||||
/// Convert ls syntax time to System Time
|
/// Convert ls syntax time to System Time
|
||||||
/// ls time has two possible syntax:
|
/// ls time has two possible syntax:
|
||||||
/// 1. if year is current: %b %d %H:%M (e.g. Nov 5 13:46)
|
/// 1. if year is current: %b %d %H:%M (e.g. Nov 5 13:46)
|
||||||
/// 2. else: %b %d %Y (e.g. Nov 5 2019)
|
/// 2. else: %b %d %Y (e.g. Nov 5 2019)
|
||||||
pub fn lstime_to_systime(
|
pub fn parse_lstime(tm: &str, fmt_year: &str, fmt_hours: &str) -> Result<SystemTime, ParseError> {
|
||||||
tm: &str,
|
|
||||||
fmt_year: &str,
|
|
||||||
fmt_hours: &str,
|
|
||||||
) -> Result<SystemTime, ParseError> {
|
|
||||||
let datetime: NaiveDateTime = match NaiveDate::parse_from_str(tm, fmt_year) {
|
let datetime: NaiveDateTime = match NaiveDate::parse_from_str(tm, fmt_year) {
|
||||||
Ok(date) => {
|
Ok(date) => {
|
||||||
// Case 2.
|
// Case 2.
|
||||||
@@ -261,50 +179,11 @@ pub fn lstime_to_systime(
|
|||||||
.unwrap_or(SystemTime::UNIX_EPOCH))
|
.unwrap_or(SystemTime::UNIX_EPOCH))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// align_text_center
|
|
||||||
///
|
|
||||||
/// Align text to center for a given width
|
|
||||||
pub fn align_text_center(text: &str, width: u16) -> String {
|
|
||||||
let indent_size: usize = match (width as usize) >= text.len() {
|
|
||||||
// NOTE: The check prevents underflow
|
|
||||||
true => (width as usize - text.len()) / 2,
|
|
||||||
false => 0,
|
|
||||||
};
|
|
||||||
textwrap::indent(
|
|
||||||
text,
|
|
||||||
(0..indent_size).map(|_| " ").collect::<String>().as_str(),
|
|
||||||
)
|
|
||||||
.trim_end()
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// ### hash_sha256_file
|
|
||||||
///
|
|
||||||
/// Get SHA256 of provided path
|
|
||||||
pub fn hash_sha256_file(file: &Path) -> Result<String, std::io::Error> {
|
|
||||||
// Open file
|
|
||||||
let mut reader: File = File::open(file)?;
|
|
||||||
let mut context = Context::new(&SHA256);
|
|
||||||
let mut buffer = [0; 8192];
|
|
||||||
loop {
|
|
||||||
let count = reader.read(&mut buffer)?;
|
|
||||||
if count == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
context.update(&buffer[..count]);
|
|
||||||
}
|
|
||||||
// Finish context
|
|
||||||
let digest: Digest = context.finish();
|
|
||||||
Ok(HEXLOWER.encode(digest.as_ref()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_utils_parse_remote_opt() {
|
fn test_utils_parse_remote_opt() {
|
||||||
// Base case
|
// Base case
|
||||||
@@ -395,31 +274,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_utils_fmt_pex() {
|
fn test_utils_parse_lstime() {
|
||||||
assert_eq!(fmt_pex(7, 7, 7), String::from("rwxrwxrwx"));
|
|
||||||
assert_eq!(fmt_pex(7, 5, 5), String::from("rwxr-xr-x"));
|
|
||||||
assert_eq!(fmt_pex(6, 6, 6), String::from("rw-rw-rw-"));
|
|
||||||
assert_eq!(fmt_pex(6, 4, 4), String::from("rw-r--r--"));
|
|
||||||
assert_eq!(fmt_pex(6, 0, 0), String::from("rw-------"));
|
|
||||||
assert_eq!(fmt_pex(0, 0, 0), String::from("---------"));
|
|
||||||
assert_eq!(fmt_pex(4, 4, 4), String::from("r--r--r--"));
|
|
||||||
assert_eq!(fmt_pex(1, 2, 1), String::from("--x-w---x"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_utils_time_to_str() {
|
|
||||||
let system_time: SystemTime = SystemTime::from(SystemTime::UNIX_EPOCH);
|
|
||||||
assert_eq!(
|
|
||||||
time_to_str(system_time, "%Y-%m-%d"),
|
|
||||||
String::from("1970-01-01")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_utils_lstime_to_systime() {
|
|
||||||
// Good cases
|
// Good cases
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lstime_to_systime("Nov 5 16:32", "%b %d %Y", "%b %d %H:%M")
|
parse_lstime("Nov 5 16:32", "%b %d %Y", "%b %d %H:%M")
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
@@ -428,7 +286,7 @@ mod tests {
|
|||||||
Duration::from_secs(1604593920)
|
Duration::from_secs(1604593920)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lstime_to_systime("Dec 2 21:32", "%b %d %Y", "%b %d %H:%M")
|
parse_lstime("Dec 2 21:32", "%b %d %Y", "%b %d %H:%M")
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
@@ -437,7 +295,7 @@ mod tests {
|
|||||||
Duration::from_secs(1606944720)
|
Duration::from_secs(1606944720)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lstime_to_systime("Nov 5 2018", "%b %d %Y", "%b %d %H:%M")
|
parse_lstime("Nov 5 2018", "%b %d %Y", "%b %d %H:%M")
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
@@ -446,7 +304,7 @@ mod tests {
|
|||||||
Duration::from_secs(1541376000)
|
Duration::from_secs(1541376000)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lstime_to_systime("Mar 18 2018", "%b %d %Y", "%b %d %H:%M")
|
parse_lstime("Mar 18 2018", "%b %d %Y", "%b %d %H:%M")
|
||||||
.ok()
|
.ok()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
@@ -455,50 +313,8 @@ mod tests {
|
|||||||
Duration::from_secs(1521331200)
|
Duration::from_secs(1521331200)
|
||||||
);
|
);
|
||||||
// bad cases
|
// bad cases
|
||||||
assert!(lstime_to_systime("Oma 31 2018", "%b %d %Y", "%b %d %H:%M").is_err());
|
assert!(parse_lstime("Oma 31 2018", "%b %d %Y", "%b %d %H:%M").is_err());
|
||||||
assert!(lstime_to_systime("Feb 31 2018", "%b %d %Y", "%b %d %H:%M").is_err());
|
assert!(parse_lstime("Feb 31 2018", "%b %d %Y", "%b %d %H:%M").is_err());
|
||||||
assert!(lstime_to_systime("Feb 15 25:32", "%b %d %Y", "%b %d %H:%M").is_err());
|
assert!(parse_lstime("Feb 15 25:32", "%b %d %Y", "%b %d %H:%M").is_err());
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_utils_align_text_center() {
|
|
||||||
assert_eq!(
|
|
||||||
align_text_center("hello world!", 24),
|
|
||||||
String::from(" hello world!")
|
|
||||||
);
|
|
||||||
// Bad case
|
|
||||||
assert_eq!(
|
|
||||||
align_text_center("hello world!", 8),
|
|
||||||
String::from("hello world!")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn test_utils_fmt_millis() {
|
|
||||||
assert_eq!(
|
|
||||||
fmt_millis(Duration::from_millis(2048)),
|
|
||||||
String::from("2.048")
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
fmt_millis(Duration::from_millis(8192)),
|
|
||||||
String::from("8.192")
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
fmt_millis(Duration::from_millis(18192)),
|
|
||||||
String::from("18.192")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_utils_hash_sha256() {
|
|
||||||
let tmp: tempfile::NamedTempFile = tempfile::NamedTempFile::new().unwrap();
|
|
||||||
// Write
|
|
||||||
let mut fhnd: File = File::create(tmp.path()).unwrap();
|
|
||||||
assert!(fhnd.write_all(b"Hello world!\n").is_ok());
|
|
||||||
assert_eq!(
|
|
||||||
*hash_sha256_file(tmp.path()).ok().as_ref().unwrap(),
|
|
||||||
String::from("0ba904eae8773b70c75333db4de2f3ac45a8ad4ddba1b242f0b3cfc199391dd8")
|
|
||||||
);
|
|
||||||
// Bad file
|
|
||||||
assert!(hash_sha256_file(Path::new("/tmp/oiojjt5ig/aiehgoiwg")).is_err());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user