From 9206200515eef42dfd87cba3adf6fe7afa1ac675 Mon Sep 17 00:00:00 2001 From: ge Date: Mon, 13 Jan 2025 19:32:42 +0300 Subject: [PATCH] breaking: replace bytes with byte, upd parser, etc --- Makefile | 4 +- README.md | 5 ++- cmd/dataunit.v | 88 ------------------------------------------ src/dataunits.v | 68 ++++++++++++++++++-------------- tests/dataunits_test.v | 34 +++++++++------- 5 files changed, 66 insertions(+), 133 deletions(-) delete mode 100644 cmd/dataunit.v diff --git a/Makefile b/Makefile index e3488b0..121abf7 100644 --- a/Makefile +++ b/Makefile @@ -15,8 +15,8 @@ serve: clean doc build: v -path "$$(realpath $$PWD/../)|@vlib|@vmodules" \ - -prod -skip-unused -parallel-cc -cflags -static -cflags -s -d no_segfault_handler \ - cmd/dataunit.v -o dataunit + -prod -parallel-cc -cflags -static -cflags -s -d no_segfault_handler \ + cmd/dataunit -o dataunit clean: rm -r $(DOC_DIR) || true diff --git a/README.md b/README.md index 7eae74b..5c8dec6 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,16 @@ Example: import dataunits fn main() { + // convert via convert fn kilobytes := dataunits.convert(500, dataunits.mbit, dataunits.kb) println(kilobytes) // 62500.0 + // convert via DataSize method (the arguments order matters) mebibytes := (dataunits.gib * 15).mib() println(mebibytes) // 15360.0 - bytes := dataunits.DataSize(2000 * dataunits.gib).bytes() + // convert via DataSize method with explicit type cast + bytes := dataunits.DataSize(2000 * dataunits.gib).byte() println(bytes) // 2.147483648e+12 == 2147483648000 } ``` diff --git a/cmd/dataunit.v b/cmd/dataunit.v deleted file mode 100644 index f9103e1..0000000 --- a/cmd/dataunit.v +++ /dev/null @@ -1,88 +0,0 @@ -// This is free and unencumbered software released into the public domain. - -// Anyone is free to copy, modify, publish, use, compile, sell, or -// distribute this software, either in source code form or as a compiled -// binary, for any purpose, commercial or non-commercial, and by any -// means. - -// In jurisdictions that recognize copyright laws, the author or authors -// of this software dedicate any and all copyright interest in the -// software to the public domain. We make this dedication for the benefit -// of the public at large and to the detriment of our heirs and -// successors. We intend this dedication to be an overt act of -// relinquishment in perpetuity of all present and future rights to this -// software under copyright law. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR -// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. - -// For more information, please refer to - -/* - dataunit - a simple CLI tool for data units convertion. -*/ - -module main - -import os -import flag -import dataunits - -@[name: 'dataunit'] -struct FlagConfig { - help bool - from string @[short: f] - to string @[short: t] -mut: - value f64 @[ignore] -} - -fn main() { - mut flags, no_matches := flag.to_struct[FlagConfig](os.args, skip: 1, style: .v) or { - eprintln('cmdline parsing error, see -help for info') - exit(2) - } - if no_matches.len > 1 { - eprintln('unrecognized arguments: ${no_matches[1..]}') - exit(2) - } else if no_matches.len == 1 { - flags.value = no_matches[0].f64() - } - if flags.help { - println('convert the value between data size units.') - println('usage: dataunit -f -t ') - println('options:') - println(' -help print this help message and exit') - println(' -f, -from source data unit') - println(' -t, -to destination data unit') - exit(0) - } - if flags.from == '' || flags.to == '' { - eprintln('no -from or -to flag set, see -help for info') - exit(2) - } - if flags.value == 0 { - eprintln('no value passed, see -help for info') - exit(2) - } - src := dataunits.from_string(flags.from, ci: true) or { - eprintln('invalid source unit: ${err}') - exit(1) - } - dst := dataunits.from_string(flags.to, ci: true) or { - eprintln('invalid destination unit: ${err}') - exit(1) - } - result := '${dataunits.convert(flags.value, src, dst):.20f}' - splitted := result.split('.') - if splitted[1].contains_only('0') { - println(splitted[0]) - } else { - println(result.trim_right('0')) - } -} diff --git a/src/dataunits.v b/src/dataunits.v index ec7c804..2d25b7b 100644 --- a/src/dataunits.v +++ b/src/dataunits.v @@ -37,8 +37,8 @@ pub fn (d DataSize) nibble() f64 { return f64(d / nibble) } -pub fn (d DataSize) bytes() f64 { - return f64(d / bytes) +pub fn (d DataSize) byte() f64 { + return f64(d / byte) } pub fn (d DataSize) kb() f64 { @@ -171,9 +171,9 @@ pub fn (d DataSize) yibit() f64 { pub const bit = DataSize(1) pub const nibble = bit * 4 -pub const bytes = bit * 8 +pub const byte = bit * 8 -pub const kb = bytes * 1000 +pub const kb = byte * 1000 pub const mb = kb * 1000 pub const gb = mb * 1000 pub const tb = gb * 1000 @@ -182,7 +182,7 @@ pub const eb = pb * 1000 pub const zb = eb * 1000 pub const yb = zb * 1000 -pub const kib = bytes * 1024 +pub const kib = byte * 1024 pub const mib = kib * 1024 pub const gib = mib * 1024 pub const tib = gib * 1024 @@ -212,7 +212,7 @@ pub const yibit = zibit * 1024 const units = { 'bit': bit 'nibble': nibble - 'bytes': bytes + 'byte': byte 'kB': kb 'MB': mb 'GB': gb @@ -282,22 +282,22 @@ pub fn convert(value f64, from DataSize, to DataSize) f64 { // from_string parses input and returns the actual DataSize. // Note: Case insensitivity makes unit abbreviations such as `Mb` (megabit) and `MB` (megabyte) -// ambiguous. Use `bit` suffix for bit units. The `b` suffix will be accepted as byte unit. +// ambiguous. Use `bit` suffix for values in bits. The `b` suffix will be accepted as byte unit. // Example: // ``` // assert dataunits.from_string('GiB')! == dataunits.gib -// assert dataunits.from_string('M')! == dataunits.mib -// assert dataunits.from_string('M', bits: true, metric: true)! == dataunits.mbit -// assert dataunits.from_string('ZeTtAbYtEs', ci: true)! == dataunits.zb +// assert dataunits.from_string('M')! == dataunits.mb +// assert dataunits.from_string('M', in_bits: true)! == dataunits.mbit +// assert dataunits.from_string('ZeTtAbYtEs', case_insensitive: true)! == dataunits.zb // ``` pub fn from_string(input string, params ParseParams) !DataSize { if !input.is_pure_ascii() { return error('${input} non-ASCII characters is not allowed in data size unit') } unit := parse_unit_str(input, params) - if params.ci { + if params.case_insensitive { for key, value in units { - if key.to_lower_ascii() == unit { + if key.to_lower_ascii() == unit.to_lower_ascii() { return value } } @@ -309,45 +309,52 @@ fn parse_unit_str(input string, params ParseParams) string { mut unit := '' match true { input.to_lower_ascii() in ['byte', 'bytes'] { - return 'bytes' + return 'byte' } input.to_lower_ascii() in ['bit', 'bits'] { return 'bit' } input.len == 1 { - if params.metric { - unit = input - } else { + if params.binary_size { unit = input.to_upper_ascii() + 'i' + } else { + unit = input } - if params.bits { + if params.in_bits { unit += 'bit' } else { unit += 'B' } return unit } - input.len == 2 && input[1] == u8(`b`) && params.ci == false { - if input[0] != u8(`k`) { - return input[..1] + 'bit' + input.len == 2 && input[1] == u8(`i`) { + if params.in_bits { + return input + 'bit' } - return input[..1].to_upper_ascii() + 'bit' + return input + 'B' + } + input.len == 3 && input.ends_with('ib') && params.case_insensitive == false { + // Mib -> Mibit + return input[..2] + 'bit' + } + input.len == 2 && input[1] == u8(`b`) && params.case_insensitive == false { + // Mb -> Mbit + return input[..1] + 'bit' } else { unit = input } } - if params.ci { + if params.case_insensitive { unit = unit.to_lower_ascii() } if unit.len == 5 && unit.ends_with('ibit') { // prevent Gibit --> Git transform return unit } - unit = unit.replace_each(maps.flat_map[string, string, string](prefixes, |k, v| [ - k, - v, - ])) + // transform full names to short ones: megabits --> mbit, etc. + prefixes_array := maps.flat_map[string, string, string](prefixes, |k, v| [k, v]) + unit = unit.replace_each(prefixes_array) unit = unit.replace_each(['bytes', 'B', 'byte', 'B']).replace_once('bits', 'bit') return unit } @@ -355,9 +362,12 @@ fn parse_unit_str(input string, params ParseParams) string { @[params] pub struct ParseParams { pub: - ci bool // if true parse string in case insensitive mode - bits bool // if true interpret single letter abbreviations as bit, otherwise as byte - metric bool // if ture apply single letter as metric prefix (power of ten), otherwise as binary + // if true parse string in case insensitive mode + case_insensitive bool + // if true accept input such 'M', 'Gi' as bits, otherwise as bytes + in_bits bool + // if true accept single letter prefix as binary (power of two), otherwise as metric prefix + binary_size bool } // to_string returns a string representation of data size unit in short form diff --git a/tests/dataunits_test.v b/tests/dataunits_test.v index 8d18710..3fe0fef 100644 --- a/tests/dataunits_test.v +++ b/tests/dataunits_test.v @@ -1,31 +1,39 @@ import dataunits fn test_convert() { - assert (dataunits.nibble * 4).bytes() == 2 + assert (dataunits.nibble * 4).byte() == 2 assert (dataunits.bit * 8).bit() == 8 - assert (dataunits.bit * 8).bytes() == 1 + assert (dataunits.bit * 8).byte() == 1 assert (dataunits.gib * 10).mib() == 10240 - assert (dataunits.gib * 5000).bytes() == i64(5368709120000) + assert (dataunits.gib * 5000).byte() == i64(5368709120000) assert (dataunits.mbit * 500).kb() == 62500 assert dataunits.convert(500, dataunits.mbit, dataunits.kb) == 62500 - assert dataunits.DataSize(4000 * dataunits.gib).bytes() == f64(4294967296000) + assert dataunits.DataSize(4000 * dataunits.gib).byte() == f64(4294967296000) } fn test_from_string() { assert dataunits.from_string('GiB')! == dataunits.gib - assert dataunits.from_string('M')! == dataunits.mib - assert dataunits.from_string('m', ci: true)! == dataunits.mib - assert dataunits.from_string('M', bits: true, metric: true)! == dataunits.mbit - assert dataunits.from_string('ZeTtAbYtEs', ci: true)! == dataunits.zb - assert dataunits.from_string('bytes')! == dataunits.bytes - assert dataunits.from_string('byte')! == dataunits.bytes + assert dataunits.from_string('M')! == dataunits.mb + assert dataunits.from_string('m', case_insensitive: true)! == dataunits.mb + assert dataunits.from_string('M', binary_size: true)! == dataunits.mib + assert dataunits.from_string('M', in_bits: true)! == dataunits.mbit + assert dataunits.from_string('ZeTtAbYtEs', case_insensitive: true)! == dataunits.zb + assert dataunits.from_string('bytes')! == dataunits.byte + assert dataunits.from_string('byte')! == dataunits.byte assert dataunits.from_string('megabytes')! == dataunits.mb assert dataunits.from_string('megabyte')! == dataunits.mb assert dataunits.from_string('exbibit')! == dataunits.eibit assert dataunits.from_string('Mb')! == dataunits.mbit assert dataunits.from_string('MB')! == dataunits.mb - assert dataunits.from_string('Mb', ci: true)! == dataunits.mb + assert dataunits.from_string('Mb', case_insensitive: true)! == dataunits.mb assert dataunits.from_string('Gibit')! == dataunits.gibit - assert dataunits.from_string('gibit', ci: true)! == dataunits.gibit - assert dataunits.from_string('Gib', ci: true)! == dataunits.gib + assert dataunits.from_string('gibit', case_insensitive: true)! == dataunits.gibit + assert dataunits.from_string('gIBiT', case_insensitive: true)! == dataunits.gibit + assert dataunits.from_string('Gib', case_insensitive: true)! == dataunits.gib + assert dataunits.from_string('Gib')! == dataunits.gibit + assert dataunits.from_string('k')! == dataunits.kb + assert dataunits.from_string('kB')! == dataunits.kb + assert dataunits.from_string('kb')! == dataunits.kbit + assert dataunits.from_string('Ki')! == dataunits.kib + assert dataunits.from_string('Gi')! == dataunits.gib }