Utils into multiple files

This commit is contained in:
ChristianVisintin
2020-12-20 15:36:48 +01:00
parent 77545ec87d
commit eb12da0308
12 changed files with 316 additions and 223 deletions

View File

@@ -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",

View File

@@ -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",

View File

@@ -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")
) )
); );
} }

View File

@@ -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);

View File

@@ -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);
} }

View File

@@ -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},

View File

@@ -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();

View File

@@ -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
View 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
View 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
View 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;

View File

@@ -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());
} }
} }