├── .gitignore ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── justfile └── src ├── lib.rs ├── main.rs ├── operations.rs └── run.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quick-calc" 3 | version = "0.3.0" 4 | description = "A cli tool for quick decimal, hexadecimal, binary, and octal basic formatting and calculations" 5 | authors = ["RJ Rybarczyk "] 6 | homepage = "https://github.com/rrybarczyk/quick-calc" 7 | license = "CC0-1.0" 8 | readme = "README.md" 9 | edition = "2018" 10 | 11 | [[bin]] 12 | name = "qcal" 13 | path = "src/main.rs" 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | rdbg: 2 | cargo run -- format 4 3 | cargo run -- format 5 6 4 | 5 | testdbg: 6 | cargo test -- --nocapture 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quick-calc 2 | A cli tool for quick decimal, hexadecimal, binary, and octal basic formatting and calculations. 3 | 4 | ``` 5 | $ cargo install quick-calc 6 | ``` 7 | 8 | ## Examples 9 | 10 | ### Format as Decimal, Hexadecimal, Octal, and Binary 11 | ``` 12 | $ qcal format 0xdeadbeef 13 | > dec: 3735928559 hex: 0xdeadbeef oct: o33653337357 bin: b11011110101011011011111011101111 14 | ``` 15 | 16 | ``` 17 | $ qcal format 0x1234 234 b011101 o24 18 | > dec: 4660 hex: 0x1234 oct: o11064 bin: b1001000110100 19 | > dec: 234 hex: 0xea oct: o352 bin: b11101010 20 | > dec: 29 hex: 0x1d oct: o35 bin: b11101 21 | > dec: 20 hex: 0x14 oct: o24 bin: b10100 22 | ``` 23 | 24 | ### Swap Endianness of Hexadecimal 25 | ``` 26 | $ qcal endian 0x55bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000 27 | > 0x000000002a22cfee1f2c846adbd12b3e183d4f97683f85dad08a79780a84bd55 28 | ``` 29 | 30 | ``` 31 | $ qcal endian 0xdeadbeef abcdef56 32 | > 0xefbeadde 33 | > 0x56efcdab 34 | ``` 35 | 36 | ### Count Byte Length of Hexadecimal 37 | ``` 38 | $ qcal bytelen 0xdeadbeef 39 | > 0xdeadbeef: 4 bytes 40 | ``` 41 | 42 | ``` 43 | $ qcal bytelen 0xdeadbeef 0x12 44 | > 0xdeadbeef: 4 bytes 45 | > 0x12: 1 byte 46 | ``` 47 | You can use `bytelen` or `len` to access this operation. 48 | 49 | ### Count Character Length 50 | ``` 51 | $ qcal charlen 0x1234 234 b011101 o24 4 52 | > 0x1234: 4 chars 53 | > 234: 3 chars 54 | > b011101: 6 chars 55 | > o24: 2 chars 56 | ``` 57 | 58 | ### Add 59 | ``` 60 | $ qcal add 0xFF 30 o24 b111 61 | > dec: 312 hex: 138 oct: 470 bin: 100111000 62 | ``` 63 | 64 | ``` 65 | $ qcal add 30 2 5 66 | > dec: 37 hex: 25 oct: 45 bin: 100101 67 | ``` 68 | 69 | ``` 70 | $ qcal add 21 14 71 | > dec: 35 hex: 23 oct: 43 bin: 100011 72 | ``` 73 | 74 | ### Subtract 75 | ``` 76 | $ qcal sub 0xFF 30 o24 b111 77 | > dec: 198 hex: c6 oct: 306 bin: 11000110 78 | ``` 79 | 80 | ``` 81 | $ qcal sub 30 2 5 82 | > dec: 23 hex: 17 oct: 27 bin: 10111 83 | ``` 84 | 85 | ``` 86 | $ qcal sub 21 14 87 | > dec: 7 hex: 7 oct: 7 bin: 111 88 | ``` 89 | 90 | ### Multiply 91 | ``` 92 | $ qcal mul 0xFF 30 o24 b111 93 | > dec: 1071000 hex: 105798 oct: 4053630 bin: 100000101011110011000 94 | ``` 95 | 96 | ``` 97 | $ qcal mul 30 2 5 98 | > dec: 300 hex: 12c oct: 454 bin: 100101100 99 | ``` 100 | 101 | ``` 102 | $ qcal mul 21 14 103 | > dec: 294 hex: 126 oct: 446 bin: 100100110 104 | ``` 105 | 106 | ### Divide 107 | ``` 108 | $ qcal div 0xFF 30 o24 b111 109 | > dec: 0 hex: 0 oct: 0 bin: 0 110 | ``` 111 | 112 | ``` 113 | $ qcal div 30 2 5 114 | > dec: 3 hex: 3 oct: 3 bin: 11 115 | ``` 116 | 117 | ``` 118 | $ qcal mul 21 14 119 | > dec: 1 hex: 1 oct: 1 bin: 1 120 | ``` 121 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | check: 2 | cargo watch --clear --exec check 3 | 4 | test: 5 | cargo watch --clear --exec test 6 | 7 | tpr: 8 | watchexec -i target make testdbg 9 | 10 | test-print: 11 | cargo test -- --nocapture 12 | 13 | build: 14 | cargo build --release 15 | 16 | r: 17 | watchexec -i target make rdbg 18 | 19 | run: 20 | cargo build --release 21 | ./target/release/qcal format 4 22 | ./target/release/qcal format 4 5 23 | ./target/release/qcal format 0xDEADBEEF 3 o24 b1101101 24 | ./target/release/qcal endian 55bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000 25 | ./target/release/qcal endian 29263ed541e0072216baa08ead2d787754cb882f573543a10000486e00000000 26 | ./target/release/qcal endian abcdef 27 | ./target/release/qcal add 0xFF 30 o24 b111 28 | ./target/release/qcal sub 0xFF 30 o24 b111 29 | ./target/release/qcal mul 0xFF 30 o24 b111 30 | ./target/release/qcal div 0xFF 30 o24 b111 31 | ./target/release/qcal bytelen 0xFF 0xDEADBEEF 32 | ./target/release/qcal charlen 0xFF 0xDEADBEEF 33 | 34 | publish: 35 | cargo build 36 | cargo publish 37 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod operations; 2 | mod run; 3 | pub use crate::run::run; 4 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use quick_calc; 2 | 3 | fn main() { 4 | quick_calc::run().unwrap(); 5 | } 6 | -------------------------------------------------------------------------------- /src/operations.rs: -------------------------------------------------------------------------------- 1 | /// Print the cli args formatted as dec, hex, oct, and bin. 2 | pub fn format_args(args: &mut Vec) { 3 | for arg in args { 4 | let sarg = strip_prefix(arg); 5 | print_calc(isize::from_str_radix(&sarg.0, sarg.1).unwrap()); 6 | } 7 | } 8 | 9 | 10 | /// Calculate the specified operation and print. 11 | pub fn calculate_operation(args: &mut Vec, func: F) 12 | where F: std::ops::FnMut(isize, &isize) -> isize { 13 | 14 | let mut var_buf: Vec = Vec::new(); 15 | for arg in args { 16 | let sarg = strip_prefix(arg); 17 | var_buf.push(isize::from_str_radix(&sarg.0, sarg.1).unwrap()); 18 | } 19 | let vec2 = var_buf.split_off(1); 20 | print_calc(vec2.iter().fold(var_buf[0], func)); 21 | } 22 | 23 | /// Swap the endianness of a hex string and print. 24 | pub fn swap_endianness(args: &mut Vec) { 25 | 26 | for arg in args { 27 | println!("0x{}", swap(&arg)); 28 | } 29 | } 30 | 31 | /// Calculate the number of bytes or characters of a hex string and print. 32 | pub fn calculate_length(args: &mut Vec, is_bytes: bool) { 33 | for arg in args { 34 | if is_bytes { 35 | let arg_count = ((get_char_length(arg) as f64)/2.0).ceil() as usize; 36 | print_len(arg.to_string(), arg_count, is_bytes); 37 | } else { 38 | print_len(arg.to_string(), get_char_length(arg), is_bytes); 39 | } 40 | } 41 | } 42 | 43 | /// Removes the `x`, `o`, `b`, or `*x` prefix if it exists, and returns a 44 | /// tuple of the number as a string and its base. 45 | fn strip_prefix(s: &str) -> (&str, u32) { 46 | let len = s.len(); 47 | let bytes = s.as_bytes(); 48 | 49 | if bytes[0] == b'x' || bytes[0] == b'X' { 50 | return (&s[1..len], 16); 51 | } else if bytes[0] == b'o' || bytes[0] == b'O' { 52 | return (&s[1..len], 8); 53 | } else if bytes[0] == b'b' || bytes[0] == b'B' { 54 | return (&s[1..len], 2); 55 | } else if len > 1 && (bytes[1] == b'x' || bytes[1] == b'X') { 56 | return (&s[2..len], 16); 57 | } 58 | (&s[..], 10) 59 | } 60 | 61 | /// Swap the endianness of a hex string. 62 | fn swap(arg: &str) -> String { 63 | let mut tmp_c = 0 as char; 64 | let mut be_slice: Vec = Vec::new(); 65 | 66 | let sarg = strip_prefix(arg); 67 | 68 | for (idx, c) in sarg.0.chars().rev().enumerate() { 69 | if idx % 2 != 0 { 70 | be_slice.push(c); 71 | be_slice.push(tmp_c); 72 | } else { 73 | tmp_c = c; 74 | } 75 | } 76 | be_slice.iter().collect() 77 | } 78 | 79 | /// Calculate the number of characters in a hex string. 80 | fn get_char_length(arg: &str) -> usize { 81 | strip_prefix(&arg).0.to_string().len() 82 | } 83 | 84 | 85 | /// Print the result formatted as dec, hex, oct, and bin. 86 | fn print_calc(result: isize) { 87 | println!("dec: {}\t\thex: 0x{:x}\t\toct: o{:o}\t\tbin: b{:b}", result, result, result, result); 88 | } 89 | 90 | /// Print the number of bytes or characters of a hex string. 91 | fn print_len(arg: String, len: usize, is_bytes: bool) { 92 | let len_type = if is_bytes { "byte" } else { "char" }; 93 | 94 | if len == 1 || len == 0 { 95 | println!("{}:\t 1 {}", arg, len_type); 96 | } else { 97 | println!("{}:\t {} {}s", arg, len, len_type); 98 | } 99 | } 100 | 101 | #[cfg(test)] 102 | mod tests { 103 | use super::*; 104 | 105 | #[test] 106 | fn test_calculate_char_length() { 107 | dbg!("needs better tests"); 108 | let mut args = create_deadbeef_vector(true); 109 | calculate_length(&mut args, false); 110 | calculate_length(&mut args, true); 111 | assert!(true); 112 | } 113 | 114 | #[test] 115 | fn test_swap() { 116 | let hex0a = "000000002a22cfee1f2c846adbd12b3e183d4f97683f85dad08a79780a84bd55"; 117 | let hex0b = "55bd840a78798ad0da853f68974f3d183e2bd1db6a842c1feecf222a00000000"; 118 | assert_eq!(swap(&hex0a), hex0b); 119 | assert_eq!(swap(&hex0b), hex0a); 120 | 121 | let hex1a = "abcdef"; 122 | let hex1b = "efcdab"; 123 | assert_eq!(swap(&hex1a), hex1b); 124 | assert_eq!(swap(&hex1b), hex1a); 125 | } 126 | 127 | #[test] 128 | fn test_get_char_length() { 129 | let arg = String::from("0xdeadbeef"); 130 | assert_eq!(get_char_length(&arg), 8); 131 | } 132 | 133 | #[test] 134 | fn test_strip_prefix() { 135 | let arg = String::from("0xdeadbeef"); 136 | assert_eq!(strip_prefix(&arg).0, "deadbeef"); 137 | } 138 | 139 | fn create_deadbeef_vector(prefix: bool) -> Vec { 140 | let mut db_dec: String; 141 | let mut db_hex: String; 142 | let mut db_oct: String; 143 | let mut db_bin: String; 144 | 145 | if prefix { 146 | db_dec = String::from("3735928559"); 147 | db_hex = String::from("0xdeadbeef"); 148 | db_oct = String::from("o33653337357"); 149 | db_bin = String::from("b11011110101011011011111011101111"); 150 | } else { 151 | db_dec = String::from("3735928559"); 152 | db_hex = String::from("deadbeef"); 153 | db_oct = String::from("33653337357"); 154 | db_bin = String::from("11011110101011011011111011101111"); 155 | } 156 | vec![db_dec, db_hex, db_oct, db_bin] 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/run.rs: -------------------------------------------------------------------------------- 1 | use crate::operations; 2 | 3 | pub fn run() -> Result<(), ()> { 4 | // Define valid cli commands 5 | let commands = ["format", "add", "sub", "mul", "div", "endian", "len", "bytelen", "charlen"]; 6 | 7 | // Grab useful cli args 8 | let mut args = std::env::args().collect::>(); 9 | args.remove(0); 10 | 11 | // If no arguments are given, panic. 12 | if args.is_empty() { 13 | panic!("Give me some numbers to format"); 14 | } 15 | 16 | // If an invliad command is given, panic. 17 | if !args.iter().any(|x| commands.contains(&x.as_str())) { 18 | panic!("Give me a valid command"); 19 | } 20 | 21 | // If a valid command but no arguments are given, panic. 22 | if args.iter().any(|x| commands.contains(&x.as_str())) && args.len() == 1 { 23 | panic!("Give me some numbers to format"); 24 | } 25 | 26 | match args.remove(0).as_str() { 27 | "format" => operations::format_args(&mut args), 28 | "add" => operations::calculate_operation(&mut args, |acc, x| acc + x), 29 | "sub" => operations::calculate_operation(&mut args, |acc, x| acc - x), 30 | "mul" => operations::calculate_operation(&mut args, |acc, x| acc * x), 31 | "div" => operations::calculate_operation(&mut args, |acc, x| acc / x), 32 | "len" | "bytelen" => operations::calculate_length(&mut args, true), 33 | "charlen" => operations::calculate_length(&mut args, false), 34 | "endian" => operations::swap_endianness(&mut args), 35 | _ => panic!("Give me a valid command"), 36 | }; 37 | Ok(()) 38 | } 39 | --------------------------------------------------------------------------------