mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-05-21 19:13:57 +00:00
Remove unused dependency and simple feature, update dependencies and fix some clippy lints
This commit is contained in:
parent
0de52c6c99
commit
9cca64003a
28 changed files with 229 additions and 303 deletions
|
@ -174,10 +174,9 @@ fn invite_user(data: Json<InviteData>, _token: AdminToken, conn: DbConn) -> Empt
|
|||
#[post("/test/smtp", data = "<data>")]
|
||||
fn test_smtp(data: Json<InviteData>, _token: AdminToken) -> EmptyResult {
|
||||
let data: InviteData = data.into_inner();
|
||||
let email = data.email.clone();
|
||||
|
||||
if CONFIG.mail_enabled() {
|
||||
mail::send_test(&email)
|
||||
mail::send_test(&data.email)
|
||||
} else {
|
||||
err!("Mail is not enabled")
|
||||
}
|
||||
|
|
|
@ -50,7 +50,6 @@ fn get_folder(uuid: String, headers: Headers, conn: DbConn) -> JsonResult {
|
|||
|
||||
#[derive(Deserialize)]
|
||||
#[allow(non_snake_case)]
|
||||
|
||||
pub struct FolderData {
|
||||
pub Name: String,
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ mod accounts;
|
|||
mod ciphers;
|
||||
mod folders;
|
||||
mod organizations;
|
||||
pub(crate) mod two_factor;
|
||||
pub mod two_factor;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
let mut mod_routes = routes![
|
||||
|
|
|
@ -2,7 +2,6 @@ use chrono::Utc;
|
|||
use data_encoding::BASE64;
|
||||
use rocket::Route;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
|
||||
use crate::api::core::two_factor::_generate_recover_code;
|
||||
use crate::api::{ApiResult, EmptyResult, JsonResult, JsonUpcase, PasswordData};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use rocket::Route;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
|
||||
use crate::api::core::two_factor::_generate_recover_code;
|
||||
use crate::api::{EmptyResult, JsonResult, JsonUpcase, PasswordData};
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use data_encoding::BASE32;
|
||||
use rocket::Route;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::api::{JsonResult, JsonUpcase, NumberOrString, PasswordData};
|
||||
|
@ -12,11 +11,11 @@ use crate::db::{
|
|||
DbConn,
|
||||
};
|
||||
|
||||
pub(crate) mod authenticator;
|
||||
pub(crate) mod duo;
|
||||
pub(crate) mod email;
|
||||
pub(crate) mod u2f;
|
||||
pub(crate) mod yubikey;
|
||||
pub mod authenticator;
|
||||
pub mod duo;
|
||||
pub mod email;
|
||||
pub mod u2f;
|
||||
pub mod yubikey;
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
let mut routes = routes![
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use once_cell::sync::Lazy;
|
||||
use rocket::Route;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
use serde_json::Value;
|
||||
use u2f::messages::{RegisterResponse, SignResponse, U2fSignRequest};
|
||||
use u2f::protocol::{Challenge, U2f};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use rocket::Route;
|
||||
use rocket_contrib::json::Json;
|
||||
use serde_json;
|
||||
use serde_json::Value;
|
||||
use yubico::config::Config;
|
||||
use yubico::verify;
|
||||
|
|
|
@ -182,7 +182,7 @@ struct Icon {
|
|||
}
|
||||
|
||||
impl Icon {
|
||||
fn new(priority: u8, href: String) -> Self {
|
||||
const fn new(priority: u8, href: String) -> Self {
|
||||
Self { href, priority }
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ fn get_icon_url(domain: &str) -> Result<(Vec<Icon>, String), Error> {
|
|||
let mut cookie_str = String::new();
|
||||
|
||||
let resp = get_page(&ssldomain).or_else(|_| get_page(&httpdomain));
|
||||
if let Ok(mut content) = resp {
|
||||
if let Ok(content) = resp {
|
||||
// Extract the URL from the respose in case redirects occured (like @ gitlab.com)
|
||||
let url = content.url().clone();
|
||||
|
||||
|
@ -235,7 +235,7 @@ fn get_icon_url(domain: &str) -> Result<(Vec<Icon>, String), Error> {
|
|||
|
||||
// 512KB should be more than enough for the HTML, though as we only really need
|
||||
// the HTML header, it could potentially be reduced even further
|
||||
let limited_reader = crate::util::LimitedReader::new(&mut content, 512 * 1024);
|
||||
let limited_reader = content.take(512 * 1024);
|
||||
|
||||
let soup = Soup::from_reader(limited_reader)?;
|
||||
// Search for and filter
|
||||
|
|
|
@ -216,8 +216,7 @@ fn twofactor_auth(
|
|||
|
||||
let selected_twofactor = twofactors
|
||||
.into_iter()
|
||||
.filter(|tf| tf.atype == selected_id && tf.enabled)
|
||||
.nth(0);
|
||||
.find(|tf| tf.atype == selected_id && tf.enabled);
|
||||
|
||||
use crate::api::core::two_factor as _tf;
|
||||
use crate::crypto::ct_eq;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mod admin;
|
||||
pub(crate) mod core;
|
||||
pub mod core;
|
||||
mod icons;
|
||||
mod identity;
|
||||
mod notifications;
|
||||
|
|
|
@ -159,11 +159,12 @@ impl Handler for WSHandler {
|
|||
|
||||
let (_id, access_token) = match path.split('?').nth(1) {
|
||||
Some(params) => {
|
||||
let mut params_iter = params.split('&').take(2);
|
||||
let params_iter = params.split('&').take(2);
|
||||
|
||||
let mut id = None;
|
||||
let mut access_token = None;
|
||||
while let Some(val) = params_iter.next() {
|
||||
|
||||
for val in params_iter {
|
||||
if val.starts_with(ID_KEY) {
|
||||
id = Some(&val[ID_KEY.len()..]);
|
||||
} else if val.starts_with(ACCESS_TOKEN_KEY) {
|
||||
|
@ -260,7 +261,9 @@ impl Factory for WSFactory {
|
|||
// Remove handler
|
||||
if let Some(user_uuid) = &handler.user_uuid {
|
||||
if let Some(mut user_conn) = self.users.map.get_mut(user_uuid) {
|
||||
user_conn.remove_item(&handler.out);
|
||||
if let Some(pos) = user_conn.iter().position(|x| x == &handler.out) {
|
||||
user_conn.remove(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -640,7 +640,7 @@ impl Config {
|
|||
pub fn is_admin_token_set(&self) -> bool {
|
||||
let token = self.admin_token();
|
||||
|
||||
!token.is_none() && !token.unwrap().trim().is_empty()
|
||||
token.is_some() && !token.unwrap().trim().is_empty()
|
||||
}
|
||||
|
||||
pub fn render_template<T: serde::ser::Serialize>(
|
||||
|
|
|
@ -18,7 +18,7 @@ pub struct Attachment {
|
|||
|
||||
/// Local methods
|
||||
impl Attachment {
|
||||
pub fn new(id: String, cipher_uuid: String, file_name: String, file_size: i32) -> Self {
|
||||
pub const fn new(id: String, cipher_uuid: String, file_name: String, file_size: i32) -> Self {
|
||||
Self {
|
||||
id,
|
||||
cipher_uuid,
|
||||
|
@ -52,7 +52,6 @@ impl Attachment {
|
|||
|
||||
use crate::db::schema::{attachments, ciphers};
|
||||
use crate::db::DbConn;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::api::EmptyResult;
|
||||
|
|
|
@ -66,7 +66,6 @@ impl Cipher {
|
|||
|
||||
use crate::db::schema::*;
|
||||
use crate::db::DbConn;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::api::EmptyResult;
|
||||
|
|
|
@ -35,7 +35,6 @@ impl Collection {
|
|||
|
||||
use crate::db::schema::*;
|
||||
use crate::db::DbConn;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::api::EmptyResult;
|
||||
|
|
|
@ -108,7 +108,6 @@ impl Device {
|
|||
|
||||
use crate::db::schema::devices;
|
||||
use crate::db::DbConn;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::api::EmptyResult;
|
||||
|
|
|
@ -63,7 +63,6 @@ impl FolderCipher {
|
|||
|
||||
use crate::db::schema::{folders, folders_ciphers};
|
||||
use crate::db::DbConn;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::api::EmptyResult;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
use serde_json::Value;
|
||||
|
||||
|
@ -22,7 +21,7 @@ pub struct OrgPolicy {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(FromPrimitive)]
|
||||
#[derive(num_derive::FromPrimitive)]
|
||||
pub enum OrgPolicyType {
|
||||
TwoFactorAuthentication = 0,
|
||||
MasterPassword = 1,
|
||||
|
|
|
@ -34,7 +34,7 @@ pub enum UserOrgStatus {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(FromPrimitive)]
|
||||
#[derive(num_derive::FromPrimitive)]
|
||||
pub enum UserOrgType {
|
||||
Owner = 0,
|
||||
Admin = 1,
|
||||
|
@ -198,7 +198,6 @@ impl UserOrganization {
|
|||
|
||||
use crate::db::schema::{ciphers_collections, organizations, users_collections, users_organizations};
|
||||
use crate::db::DbConn;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::api::EmptyResult;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
use serde_json::Value;
|
||||
|
||||
|
@ -23,7 +22,7 @@ pub struct TwoFactor {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(FromPrimitive)]
|
||||
#[derive(num_derive::FromPrimitive)]
|
||||
pub enum TwoFactorType {
|
||||
Authenticator = 0,
|
||||
Email = 1,
|
||||
|
|
|
@ -121,7 +121,6 @@ impl User {
|
|||
use super::{Cipher, Device, Folder, TwoFactor, UserOrgType, UserOrganization};
|
||||
use crate::db::schema::{invitations, users};
|
||||
use crate::db::DbConn;
|
||||
use diesel;
|
||||
use diesel::prelude::*;
|
||||
|
||||
use crate::api::EmptyResult;
|
||||
|
@ -275,7 +274,7 @@ pub struct Invitation {
|
|||
}
|
||||
|
||||
impl Invitation {
|
||||
pub fn new(email: String) -> Self {
|
||||
pub const fn new(email: String) -> Self {
|
||||
Self { email }
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ macro_rules! make_error {
|
|||
( $( $name:ident ( $ty:ty ): $src_fn:expr, $usr_msg_fun:expr ),+ $(,)? ) => {
|
||||
const BAD_REQUEST: u16 = 400;
|
||||
|
||||
#[derive(Display)]
|
||||
pub enum ErrorKind { $($name( $ty )),+ }
|
||||
pub struct Error { message: String, error: ErrorKind, error_code: u16 }
|
||||
|
||||
|
@ -48,7 +47,7 @@ use u2f::u2ferror::U2fError as U2fErr;
|
|||
use yubico::yubicoerror::YubicoError as YubiErr;
|
||||
use lettre::smtp::error::Error as LettreErr;
|
||||
|
||||
#[derive(Display, Serialize)]
|
||||
#[derive(Serialize)]
|
||||
pub struct Empty {}
|
||||
|
||||
// Error struct
|
||||
|
@ -118,7 +117,7 @@ impl Error {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_code(mut self, code: u16) -> Self {
|
||||
pub const fn with_code(mut self, code: u16) -> Self {
|
||||
self.error_code = code;
|
||||
self
|
||||
}
|
||||
|
@ -146,7 +145,7 @@ impl<S> MapResult<S> for Option<S> {
|
|||
}
|
||||
}
|
||||
|
||||
fn _has_source<T>(e: T) -> Option<T> {
|
||||
const fn _has_source<T>(e: T) -> Option<T> {
|
||||
Some(e)
|
||||
}
|
||||
fn _no_source<T, S>(_: T) -> Option<S> {
|
||||
|
|
26
src/mail.rs
26
src/mail.rs
|
@ -46,7 +46,7 @@ fn mailer() -> SmtpTransport {
|
|||
|
||||
let smtp_client = match CONFIG.smtp_auth_mechanism() {
|
||||
Some(mechanism) => {
|
||||
let correct_mechanism = format!("\"{}\"", crate::util::upcase_first(&mechanism.trim_matches('"')));
|
||||
let correct_mechanism = format!("\"{}\"", crate::util::upcase_first(mechanism.trim_matches('"')));
|
||||
|
||||
match serde_json::from_str::<SmtpAuthMechanism>(&correct_mechanism) {
|
||||
Ok(auth_mechanism) => smtp_client.authentication_mechanism(auth_mechanism),
|
||||
|
@ -95,7 +95,7 @@ pub fn send_password_hint(address: &str, hint: Option<String>) -> EmptyResult {
|
|||
|
||||
let (subject, body_html, body_text) = get_text(template_name, json!({ "hint": hint, "url": CONFIG.domain() }))?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_delete_account(address: &str, uuid: &str) -> EmptyResult {
|
||||
|
@ -112,7 +112,7 @@ pub fn send_delete_account(address: &str, uuid: &str) -> EmptyResult {
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_verify_email(address: &str, uuid: &str) -> EmptyResult {
|
||||
|
@ -129,7 +129,7 @@ pub fn send_verify_email(address: &str, uuid: &str) -> EmptyResult {
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_welcome(address: &str) -> EmptyResult {
|
||||
|
@ -140,7 +140,7 @@ pub fn send_welcome(address: &str) -> EmptyResult {
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_welcome_must_verify(address: &str, uuid: &str) -> EmptyResult {
|
||||
|
@ -156,7 +156,7 @@ pub fn send_welcome_must_verify(address: &str, uuid: &str) -> EmptyResult {
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_invite(
|
||||
|
@ -188,7 +188,7 @@ pub fn send_invite(
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_invite_accepted(new_user_email: &str, address: &str, org_name: &str) -> EmptyResult {
|
||||
|
@ -201,7 +201,7 @@ pub fn send_invite_accepted(new_user_email: &str, address: &str, org_name: &str)
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_invite_confirmed(address: &str, org_name: &str) -> EmptyResult {
|
||||
|
@ -213,7 +213,7 @@ pub fn send_invite_confirmed(address: &str, org_name: &str) -> EmptyResult {
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTime, device: &str) -> EmptyResult {
|
||||
|
@ -232,7 +232,7 @@ pub fn send_new_device_logged_in(address: &str, ip: &str, dt: &NaiveDateTime, de
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_token(address: &str, token: &str) -> EmptyResult {
|
||||
|
@ -244,7 +244,7 @@ pub fn send_token(address: &str, token: &str) -> EmptyResult {
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_change_email(address: &str, token: &str) -> EmptyResult {
|
||||
|
@ -256,7 +256,7 @@ pub fn send_change_email(address: &str, token: &str) -> EmptyResult {
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
pub fn send_test(address: &str) -> EmptyResult {
|
||||
|
@ -267,7 +267,7 @@ pub fn send_test(address: &str) -> EmptyResult {
|
|||
}),
|
||||
)?;
|
||||
|
||||
send_email(&address, &subject, &body_html, &body_text)
|
||||
send_email(address, &subject, &body_html, &body_text)
|
||||
}
|
||||
|
||||
fn send_email(address: &str, subject: &str, body_html: &str, body_text: &str) -> EmptyResult {
|
||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -1,7 +1,7 @@
|
|||
#![feature(proc_macro_hygiene, vec_remove_item, try_trait, ip)]
|
||||
#![forbid(unsafe_code)]
|
||||
#![feature(proc_macro_hygiene, try_trait, ip)]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
extern crate openssl;
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
#[macro_use]
|
||||
|
@ -14,12 +14,6 @@ extern crate log;
|
|||
extern crate diesel;
|
||||
#[macro_use]
|
||||
extern crate diesel_migrations;
|
||||
#[macro_use]
|
||||
extern crate derive_more;
|
||||
#[macro_use]
|
||||
extern crate num_derive;
|
||||
|
||||
extern crate backtrace;
|
||||
|
||||
use std::{
|
||||
fs::create_dir_all,
|
||||
|
|
43
src/util.rs
43
src/util.rs
|
@ -65,13 +65,13 @@ impl Fairing for CORS {
|
|||
let req_headers = request.headers();
|
||||
|
||||
// We need to explicitly get the Origin header for Access-Control-Allow-Origin
|
||||
let req_allow_origin = CORS::valid_url(CORS::get_header(&req_headers, "Origin"));
|
||||
let req_allow_origin = CORS::valid_url(CORS::get_header(req_headers, "Origin"));
|
||||
|
||||
response.set_header(Header::new("Access-Control-Allow-Origin", req_allow_origin));
|
||||
|
||||
if request.method() == Method::Options {
|
||||
let req_allow_headers = CORS::get_header(&req_headers, "Access-Control-Request-Headers");
|
||||
let req_allow_method = CORS::get_header(&req_headers, "Access-Control-Request-Method");
|
||||
let req_allow_headers = CORS::get_header(req_headers, "Access-Control-Request-Headers");
|
||||
let req_allow_method = CORS::get_header(req_headers, "Access-Control-Request-Method");
|
||||
|
||||
response.set_header(Header::new("Access-Control-Allow-Methods", req_allow_method));
|
||||
response.set_header(Header::new("Access-Control-Allow-Headers", req_allow_headers));
|
||||
|
@ -86,14 +86,14 @@ impl Fairing for CORS {
|
|||
pub struct Cached<R>(R, &'static str);
|
||||
|
||||
impl<R> Cached<R> {
|
||||
pub fn long(r: R) -> Cached<R> {
|
||||
pub const fn long(r: R) -> Cached<R> {
|
||||
// 7 days
|
||||
Cached(r, "public, max-age=604800")
|
||||
Self(r, "public, max-age=604800")
|
||||
}
|
||||
|
||||
pub fn short(r: R) -> Cached<R> {
|
||||
pub const fn short(r: R) -> Cached<R> {
|
||||
// 10 minutes
|
||||
Cached(r, "public, max-age=600")
|
||||
Self(r, "public, max-age=600")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ impl Fairing for BetterLogging {
|
|||
let uri_subpath = request.uri().path().trim_start_matches(&CONFIG.domain_path());
|
||||
if self.0 || LOGGED_ROUTES.iter().any(|r| uri_subpath.starts_with(r)) {
|
||||
let status = response.status();
|
||||
if let Some(ref route) = request.route() {
|
||||
if let Some(route) = request.route() {
|
||||
info!(target: "response", "{} => {} {}", route, status.code, status.reason)
|
||||
} else {
|
||||
info!(target: "response", "{} {}", status.code, status.reason)
|
||||
|
@ -227,33 +227,6 @@ pub fn delete_file(path: &str) -> IOResult<()> {
|
|||
res
|
||||
}
|
||||
|
||||
pub struct LimitedReader<'a> {
|
||||
reader: &'a mut dyn std::io::Read,
|
||||
limit: usize, // In bytes
|
||||
count: usize,
|
||||
}
|
||||
impl<'a> LimitedReader<'a> {
|
||||
pub fn new(reader: &'a mut dyn std::io::Read, limit: usize) -> LimitedReader<'a> {
|
||||
LimitedReader {
|
||||
reader,
|
||||
limit,
|
||||
count: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::io::Read for LimitedReader<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
self.count += buf.len();
|
||||
|
||||
if self.count > self.limit {
|
||||
Ok(0) // End of the read
|
||||
} else {
|
||||
self.reader.read(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const UNITS: [&str; 6] = ["bytes", "KB", "MB", "GB", "TB", "PB"];
|
||||
|
||||
pub fn get_display_size(size: i32) -> String {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue