mirror of
https://github.com/nushell/nushell.git
synced 2024-11-25 18:03:51 +01:00
Add Filesize
type
This commit is contained in:
parent
ea6493c041
commit
d59f464156
@ -1,7 +1,281 @@
|
|||||||
use crate::Config;
|
use crate::{Config, FromValue, IntoValue, ShellError, Span, Type, Value};
|
||||||
use byte_unit::UnitType;
|
use byte_unit::UnitType;
|
||||||
use nu_utils::get_system_locale;
|
use nu_utils::get_system_locale;
|
||||||
use num_format::ToFormattedString;
|
use num_format::ToFormattedString;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{
|
||||||
|
fmt,
|
||||||
|
iter::{Product, Sum},
|
||||||
|
ops::{Add, Div, Mul, Neg, Rem, Sub},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct Filesize(i64);
|
||||||
|
|
||||||
|
impl Filesize {
|
||||||
|
pub const ZERO: Self = Self(0);
|
||||||
|
|
||||||
|
pub const fn new(bytes: i64) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn get(&self) -> i64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn from_unit(value: i64, unit: FilesizeUnit) -> Option<Self> {
|
||||||
|
if let Some(bytes) = value.checked_mul(unit.as_bytes() as i64) {
|
||||||
|
Some(Self(bytes))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for Filesize {
|
||||||
|
fn from(value: i64) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Filesize> for i64 {
|
||||||
|
fn from(filesize: Filesize) -> Self {
|
||||||
|
filesize.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u64> for Filesize {
|
||||||
|
type Error = <u64 as TryInto<i64>>::Error;
|
||||||
|
|
||||||
|
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
||||||
|
value.try_into().map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Filesize> for u64 {
|
||||||
|
type Error = <i64 as TryInto<u64>>::Error;
|
||||||
|
|
||||||
|
fn try_from(filesize: Filesize) -> Result<Self, Self::Error> {
|
||||||
|
filesize.0.try_into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_from {
|
||||||
|
($($ty:ty),* $(,)?) => {
|
||||||
|
$(
|
||||||
|
impl From<$ty> for Filesize {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: $ty) -> Self {
|
||||||
|
Self(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<Filesize> for $ty {
|
||||||
|
type Error = <i64 as TryInto<$ty>>::Error;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn try_from(filesize: Filesize) -> Result<Self, Self::Error> {
|
||||||
|
filesize.0.try_into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_from!(u8, i8, u16, i16, u32, i32);
|
||||||
|
|
||||||
|
impl FromValue for Filesize {
|
||||||
|
fn from_value(value: Value) -> Result<Self, ShellError> {
|
||||||
|
value.as_filesize()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expected_type() -> Type {
|
||||||
|
Type::Filesize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoValue for Filesize {
|
||||||
|
fn into_value(self, span: Span) -> Value {
|
||||||
|
Value::filesize(self.0, span)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Filesize {
|
||||||
|
type Output = Option<Self>;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
self.0.checked_add(rhs.0).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Filesize {
|
||||||
|
type Output = Option<Self>;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
self.0.checked_sub(rhs.0).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mul for Filesize {
|
||||||
|
type Output = Option<Self>;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Self) -> Self::Output {
|
||||||
|
self.0.checked_mul(rhs.0).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div for Filesize {
|
||||||
|
type Output = Option<Self>;
|
||||||
|
|
||||||
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
|
self.0.checked_div(rhs.0).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Rem for Filesize {
|
||||||
|
type Output = Option<Self>;
|
||||||
|
|
||||||
|
fn rem(self, rhs: Self) -> Self::Output {
|
||||||
|
self.0.checked_rem(rhs.0).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Neg for Filesize {
|
||||||
|
type Output = Option<Self>;
|
||||||
|
|
||||||
|
fn neg(self) -> Self::Output {
|
||||||
|
self.0.checked_neg().map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sum<Filesize> for Option<Filesize> {
|
||||||
|
fn sum<I: Iterator<Item = Filesize>>(iter: I) -> Self {
|
||||||
|
let mut sum = Filesize::ZERO;
|
||||||
|
for filesize in iter {
|
||||||
|
sum = (sum + filesize)?;
|
||||||
|
}
|
||||||
|
Some(sum)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Product<Filesize> for Option<Filesize> {
|
||||||
|
fn product<I: Iterator<Item = Filesize>>(iter: I) -> Self {
|
||||||
|
let mut product = Filesize::ZERO;
|
||||||
|
for filesize in iter {
|
||||||
|
product = (product * filesize)?;
|
||||||
|
}
|
||||||
|
Some(product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Filesize {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
format_filesize(self.0, "auto", Some(false)).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
pub enum FilesizeUnit {
|
||||||
|
B,
|
||||||
|
KB,
|
||||||
|
MB,
|
||||||
|
GB,
|
||||||
|
TB,
|
||||||
|
PB,
|
||||||
|
EB,
|
||||||
|
KiB,
|
||||||
|
MiB,
|
||||||
|
GiB,
|
||||||
|
TiB,
|
||||||
|
PiB,
|
||||||
|
EiB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FilesizeUnit {
|
||||||
|
pub const fn as_bytes(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::B => 1,
|
||||||
|
Self::KB => 10_u64.pow(3),
|
||||||
|
Self::MB => 10_u64.pow(6),
|
||||||
|
Self::GB => 10_u64.pow(9),
|
||||||
|
Self::TB => 10_u64.pow(12),
|
||||||
|
Self::PB => 10_u64.pow(15),
|
||||||
|
Self::EB => 10_u64.pow(18),
|
||||||
|
Self::KiB => 1 << 10,
|
||||||
|
Self::MiB => 1 << 20,
|
||||||
|
Self::GiB => 1 << 30,
|
||||||
|
Self::TiB => 1 << 40,
|
||||||
|
Self::PiB => 1 << 50,
|
||||||
|
Self::EiB => 1 << 60,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn as_filesize(&self) -> Filesize {
|
||||||
|
Filesize::new(self.as_bytes() as i64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::B => "B",
|
||||||
|
Self::KB => "KB",
|
||||||
|
Self::MB => "MB",
|
||||||
|
Self::GB => "GB",
|
||||||
|
Self::TB => "TB",
|
||||||
|
Self::PB => "PB",
|
||||||
|
Self::EB => "EB",
|
||||||
|
Self::KiB => "KiB",
|
||||||
|
Self::MiB => "MiB",
|
||||||
|
Self::GiB => "GiB",
|
||||||
|
Self::TiB => "TiB",
|
||||||
|
Self::PiB => "PiB",
|
||||||
|
Self::EiB => "EiB",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn is_decimal(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
FilesizeUnit::B
|
||||||
|
| FilesizeUnit::KB
|
||||||
|
| FilesizeUnit::MB
|
||||||
|
| FilesizeUnit::GB
|
||||||
|
| FilesizeUnit::TB
|
||||||
|
| FilesizeUnit::PB
|
||||||
|
| FilesizeUnit::EB => true,
|
||||||
|
FilesizeUnit::KiB
|
||||||
|
| FilesizeUnit::MiB
|
||||||
|
| FilesizeUnit::GiB
|
||||||
|
| FilesizeUnit::TiB
|
||||||
|
| FilesizeUnit::PiB
|
||||||
|
| FilesizeUnit::EiB => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn is_binary(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
FilesizeUnit::KB
|
||||||
|
| FilesizeUnit::MB
|
||||||
|
| FilesizeUnit::GB
|
||||||
|
| FilesizeUnit::TB
|
||||||
|
| FilesizeUnit::PB
|
||||||
|
| FilesizeUnit::EB => false,
|
||||||
|
FilesizeUnit::B
|
||||||
|
| FilesizeUnit::KiB
|
||||||
|
| FilesizeUnit::MiB
|
||||||
|
| FilesizeUnit::GiB
|
||||||
|
| FilesizeUnit::TiB
|
||||||
|
| FilesizeUnit::PiB
|
||||||
|
| FilesizeUnit::EiB => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FilesizeUnit {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
self.as_str().fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn format_filesize_from_conf(num_bytes: i64, config: &Config) -> String {
|
pub fn format_filesize_from_conf(num_bytes: i64, config: &Config) -> String {
|
||||||
// We need to take into account config.filesize_metric so, if someone asks for KB
|
// We need to take into account config.filesize_metric so, if someone asks for KB
|
||||||
|
@ -301,9 +301,9 @@ impl Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the inner `i64` filesize value or an error if this `Value` is not a filesize
|
/// Returns the inner `i64` filesize value or an error if this `Value` is not a filesize
|
||||||
pub fn as_filesize(&self) -> Result<i64, ShellError> {
|
pub fn as_filesize(&self) -> Result<Filesize, ShellError> {
|
||||||
if let Value::Filesize { val, .. } = self {
|
if let Value::Filesize { val, .. } = self {
|
||||||
Ok(*val)
|
Ok(Filesize::new(*val))
|
||||||
} else {
|
} else {
|
||||||
self.cant_convert_to("filesize")
|
self.cant_convert_to("filesize")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user