sta 0.2.4

A set of additions I think go well with the standard library.
Documentation
#![feature(test)]

extern crate test;
extern crate terminal_size;
extern crate snap;

/// Contains things related to displaying things in a terminal. Minimally that is. See the aesthetic module for making things look decent.
pub mod disp {
    use std::fmt;

    /// Prints anything that has a std::fmt::Display trait.
    pub fn print<T>(text: T) where T: fmt::Display {
        println!("{}", text);
        return;
    }

    /// Prints a newline.
    pub fn newline() {
        println!("");
        return;
    }

    /// Prints anything with the std::fmt::Debug trait. Useful for printing different collections.
    pub fn print_debug<T>(data: T) where T: fmt::Debug {
        println!("{:?}", data);
        return;
    }
}

/// Contains things related to getting args.
pub mod arg {
    use std::env;

    /// Returns a Vec of all arguments sent to the program.
    pub fn get_args() -> Vec<String> {
        return env::args().collect();
    }
}

/// String utilities.
pub mod stringutil {
    use std::string::String;

    /// Extract the char at a specific location in a string.
    pub fn get_char_pos(string: &String, char_loc: usize) -> char {
        let char_vec: &Vec<char> = &string.chars().collect(); // Generate a Vec containing all the chars in the provided string.
        return char_vec[char_loc]; // Return the right character from that Vec.
    }

    /// Get a empty newline string. Purely for making code look prettier.
    pub fn newline() -> String {
        return String::from("\n"); // Generate a new String from the "\n" byte.
    }
}

/// The appinfo module. Contains stuff related to the APPINFO standard this library uses. Providing a valid appinfo may be required for some functions in the future.
/// Meant to be set as a constant at the beginning of the main source file.
pub mod appinfo {
    /// A simple struct for storing appinfo data.
    #[derive(Debug, Clone, Copy)]
    pub struct AppInfo {
        pub name: &'static str, // The name of the app.
        pub author: &'static str, // It's author/authors.
        pub repository: &'static str, // A link to it's repository.
        pub crate_link: &'static str, // A link to it's crates.io page.
    }

    /// Implements for the struct AppInfo.
    impl AppInfo {
        /// Generate a new, empty appinfo.
        pub fn new() -> AppInfo {
            return AppInfo {name: "", author: "", repository: "", crate_link: ""};
        }
    }
}

/// Point: Define a point on a 2D canvas.
pub mod point {
    #[derive(Debug, Clone, Copy)]
    pub struct Point {
        pub x: i32,
        pub y: i32,
    }

    impl Point {
        pub fn new() -> Point {
            return Point {x: 0, y: 0};
        }
    }
}

/// Coordinate: Define a point on a 3D canvas.
pub mod coordinate {
    #[derive(Debug, Clone, Copy)]
    pub struct Coordinate {
        pub x: i32,
        pub y: i32,
        pub z: i32,
    }

    impl Coordinate {
        pub fn new() -> Coordinate {
            return Coordinate {x: 0, y: 0, z: 0};
        }
    }
}

/// Stuff related to making shapes. Both 2D and 3D.
pub mod shape {
    pub mod quadrilateral {
        use point::Point;

        #[derive(Debug, Clone, Copy)]
        pub struct Quadrilateral {
            pub corner: [Point; 4],
        }

        impl Quadrilateral {
            pub fn new() -> Quadrilateral {
                return Quadrilateral {corner: [Point {x: 0, y: 0}; 4]};
            }
        }
    }

    pub mod hexahedron {
        use coordinate::Coordinate;

        #[derive(Debug, Clone, Copy)]
        pub struct Hexahedron {
            pub corner: [Coordinate; 8],
        }

        impl Hexahedron {
            pub fn new() -> Hexahedron {
                return Hexahedron {corner: [Coordinate {x: 0, y: 0, z: 0}; 8]};
            }
        }
    }
}

/// Stuff for error handling and exiting smoothly.
pub mod error {
    use std::process;
    use ::disp::print;
    use ::stringutil;

    /// Defines an error.
    #[derive(Debug, Clone)]
    pub struct Error {
        pub errormessage: String,
        pub exitcode: i32,
        pub exit: bool,
    }

    /// Construct an error which exits. Really just a error template.
    pub fn const_error(errormessage: String, exitcode: i32) -> Error {
        return Error {errormessage: errormessage, exitcode: exitcode, exit: true};
    }

    /// Construct an error that doesn't exit. Really just a error template.
    pub fn const_error_noexit(errormessage: String) -> Error {
        return Error {errormessage: errormessage, exitcode: 1, exit: false};
    }

    /// Takes an error and prints everything in a nice format.
    pub fn error(error: Error) {
        print([String::from("Error: "), error.errormessage, stringutil::newline()].concat());

        // Exit if the exit var is set to true.
        if error.exit {
            process::exit(error.exitcode as i32);
        }
        return;
    }
}

/// Everything related to argument parsing and help texts.
pub mod argumentparser {
    /// Defines a argument.
    #[derive(Debug, Clone)]
    pub struct Argument {
        pub identifier: char,
        pub description: String,
    }

    /// Everything related to help texts.
    pub mod help {
        use std::string::String;
        use std::{thread, time};
        use ::argumentparser::Argument;
        use ::aesthetic::border;
        use ::disp::print;
        use ::stringutil;

        /// Takes a Vec of all valid Arguments and an usage paramater and turns it into a help text and prints it.
        pub fn help(usage: &'static str, arg_array: Vec<Argument>) {

            // Set the print pause duration.
            let duration = time::Duration::from_millis(25);

            // Make a new Vec to store each line of the help text.
            let mut help: Vec<String> = Vec::new();

            // Add the border, template and the usage text.
            help.push([border::horizontal(String::from("-")), stringutil::newline()].concat());
            help.push([String::from("Usage:\n    "), String::from(usage), stringutil::newline()].concat());
            help.push(String::from("Flags:"));

            // Add every argument.
            for argument in arg_array {
                help.push([String::from("    -"), argument.identifier.to_string(), String::from(" # "), argument.description].concat());
            }

            // Add a newline at the end.
            help.push(stringutil::newline());

            // Then finally print everything.
            for line in &help {
                print(&line);
                thread::sleep(duration);
            }
            return;
        }
    }

    /// A simple argument parser. In it's current state. You probably want to use clap instead but this is quite a lot simpler.
    pub mod parse {
        use std::collections::HashMap;
        use ::argumentparser::Argument;

        pub fn parseargs(argstring: String, valid_arg_array: Vec<Argument>) -> HashMap<String, bool> {
            // Make a new HashMap to store if an argument is given.
            let mut arg_bool_list = HashMap::new();

            // Set every argument to false as a base,
            for validarg in &valid_arg_array {
                arg_bool_list.insert(validarg.identifier.to_string(), false);
            }

            // Check every character in the provided argument string if it's valid. And if so, set it's entry to true.
            for arg in argstring.chars() {
                for validarg in &valid_arg_array {
                    if arg == validarg.identifier {
                        arg_bool_list.remove(&validarg.identifier.to_string());
                        arg_bool_list.insert(validarg.identifier.to_string(), true);
                    }

                }
            }
            return arg_bool_list;
        }
    }
}

/// This module is used for making your terminal applications look better.
pub mod aesthetic {
    pub mod border {
        use std::iter;
        use terminal_size::terminal_size;

        /// Create an horizontal border with the length of the terminal.
        pub fn horizontal(border_char: String) -> String {
            // Get the width of the terminal.
            let term_size = terminal_size().unwrap();
            let term_width: usize = (term_size.0).0 as usize;

            // Create a infinite generator of the character provided and take as many as the terminal fits.
            let border = iter::repeat(border_char).take(term_width).collect::<String>();
            return border;
        }
    }
}

/// A module containing the PointString type, a string variant comprised of a Vec<u8> which provides easy access and manipulation of individual
/// bytes with support for in memory compression of strings.
pub mod pstring {
    use std::vec::Vec;
    use std::string::String;
    use std::str;
    use std::fmt;
    use snap;

    pub const ENCODING: &'static str = "utf-8";
    const BYTEXOR: u8 = 53;
    const COMPRESSIONDEFAULT: bool = false;

    /// Handles compression of bytes and such.
    struct Compressor{}

    impl Compressor {
        /// Compress bytes if compression is enabled.
        #[inline(always)]
        pub fn compress_bytes(bytes: Vec<u8>, compression: bool) -> Vec<u8> {
            if compression {
                let mut encoder = snap::Encoder::new();
                return encoder.compress_vec(&bytes[..]).unwrap();
            } else {
                return bytes;
            }
        }

        /// Decompress bytes if compression is enabled.
        #[inline(always)]
        pub fn decompress_bytes(bytes: Vec<u8>, compression: bool) -> Vec<u8> {
            if compression {
                let mut decoder = snap::Decoder::new();
                return decoder.decompress_vec(&bytes[..]).unwrap();
            } else {
                return bytes;
            }
        }
    }

    /// The PointString, a string variant comprised of a Vec<u8> which provides easy access and manipulation of individual
    /// bytes with support for in memory compression of strings.
    #[derive(Debug, Clone)]
    pub struct PString {
        pub compression: bool,
        codepoints: Vec<u8>,
    }

    // Implement the display trait.
    impl fmt::Display for PString {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "{}", self.clone().to_string_backend())
        }
    }

    impl PString {
        /// Create a new, empty PString.
        pub fn new() -> PString {
            return PString { compression: COMPRESSIONDEFAULT, codepoints: Vec::new() };
        }

        /// Create a new PString with a Vec of bytes.
        pub fn from_bytes(codepoints: Vec<u8>) -> PString {
            return PString { compression: COMPRESSIONDEFAULT, codepoints: Compressor::compress_bytes(codepoints, COMPRESSIONDEFAULT) };
        }

        /// Set the bytes of an existing PString.
        pub fn set_bytes(self: &mut Self, codepoints: Vec<u8>) {
            self.codepoints = Compressor::compress_bytes(codepoints, self.compression);
        }

        /// Set a existing PString to a str.
        pub fn set_str(self: &mut Self, string: &'static str) {
            let chararray: Vec<char> = string.chars().collect();
            let mut newarray: Vec<u8> = Vec::new();
            for character in chararray {
                newarray.push(character as u8);
            }
            self.codepoints = Compressor::compress_bytes(newarray, self.compression);
        }

        /// Set a existing PString to a str.
        pub fn set_string(self: &mut Self, string: String) {
            let chararray: Vec<char> = string.chars().collect();
            let mut newarray: Vec<u8> = Vec::new();
            for character in chararray {
                newarray.push(character as u8);
            }
            self.codepoints = Compressor::compress_bytes(newarray, self.compression);
        }

        /// Create a PString from a str pointer.
        pub fn from_str(string: &'static str) -> PString {
            let mut returnvalue: PString = PString::new();
            for character in string.chars() {
                returnvalue.codepoints.push(character as u8);
            }
            returnvalue.autocompress();
            return returnvalue;
        }

        /// Create a PString from a String.
        pub fn from_string(string: String) -> PString {
            let mut returnvalue: PString = PString::new();
            for character in string.chars() {
                returnvalue.codepoints.push(character as u8);
            }
            returnvalue.autocompress();
            return returnvalue;
        }

        /// Backend private function to convert to a string.
        fn to_string_backend(self: &mut Self) -> String {
            let bytes = Compressor::decompress_bytes(self.codepoints.clone(), self.compression);
            return str::from_utf8(&bytes[..]).unwrap().to_string();
        }

        /// Get the individual bytes that make up a PString.
        pub fn as_bytes(self: &mut Self) -> Vec<u8> {
            return self.decompress_to_bytes();
        }

        /// Get the individual raw bytes that make up a PString.
        pub fn as_bytes_raw(self: &Self) -> Vec<u8> {
            return self.codepoints.clone();
        }

        /// Get a Vec of all chars in the string.
        pub fn chars(self: &Self) -> Vec<char> {
            let mut chararray: Vec<char> = Vec::new();
            for byte in self.decompress_to_bytes() {
                chararray.push(byte as char);
            }
            return chararray;
        }

        /// Enable compression.
        pub fn compress(self: &mut Self) {
            if self.compression == false {
                let mut encoder = snap::Encoder::new();
                self.codepoints = encoder.compress_vec(&self.codepoints[..]).unwrap();
                self.compression = true;
            }
        }

        /// Disable compression.
        pub fn decompress(self: &mut Self) {
            if self.compression == true {
                let mut decoder = snap::Decoder::new();
                self.codepoints = decoder.decompress_vec(&self.codepoints[..]).unwrap();
                self.compression = false;
            }
        }

        /// Compress a codepoints byte struct.
        #[inline(always)]
        pub fn compress_to_bytes(self: &Self) -> Vec<u8> {
            if self.compression == true {
                let mut encoder = snap::Encoder::new();
                return encoder.compress_vec(&self.codepoints[..]).unwrap();
            }
            return self.codepoints.clone();
        }

        /// Decompress a codepoints byte struct
        #[inline(always)]
        pub fn decompress_to_bytes(self: &Self) -> Vec<u8> {
            if self.compression == true {
                let mut decoder = snap::Decoder::new();
                return decoder.decompress_vec(&self.codepoints[..]).unwrap();
            }
            return self.codepoints.clone();
        }

        /// Autocompress data if enabled.
        #[inline(always)]
        pub fn autocompress(self: &mut Self) {
            if self.compression {
                let mut encoder = snap::Encoder::new();
                self.codepoints = encoder.compress_vec(&self.codepoints[..]).unwrap();
            }
        }

        /// Autodecompress data if enabled.
        #[inline(always)]
        fn autodecompress(self: &mut Self) {
            if self.compression {
                let mut decoder = snap::Decoder::new();
                self.codepoints = decoder.decompress_vec(&self.codepoints[..]).unwrap();
            }
        }
    }
}

/// Some useful utilities when dealing with numbers.
pub mod num {
    /// Get the average of a list of f64's.
    pub fn average(values: Vec<f64>) -> f64 {
        let mut total: f64 = 0_f64;
        for value in &values {
            total += value;
        }
        return total / (values.len() as f64);
    }
}

#[cfg(test)]
mod tests {
    use ::error;
    use ::aesthetic::border;
    use ::argumentparser;
    use ::argumentparser::Argument;
    use ::pstring::PString;
    use ::num;
    use test::Bencher;

    #[test]
    fn error() {
        let error = error::const_error_noexit("This is a sample error, beep beep.".to_string());
        error::error(error);
    }

    #[test]
    fn bordertest() {
        let border = border::horizontal(String::from("-"));
        println!("{}", border);
    }

    #[test]
    fn arghelptest() {
        let usage = "[PROGRAM NAME] -[CATEGORIES AND SWITCHES]";
        let arg1: Argument = Argument { identifier: 'v', description: String::from("Prints the program version") };
        let arg2: Argument = Argument { identifier: 't', description: String::from("Prints \"Rust is the best language\"") };
        let mut arg_array: Vec<Argument> = Vec::new();
        arg_array.push(arg1);
        arg_array.push(arg2);
        argumentparser::help::help(usage, arg_array);
    }

    #[test]
    fn argparser() {
        let args = String::from("-vt");
        let arg1: Argument = Argument { identifier: 'v', description: String::from("Prints the program version") };
        let arg2: Argument = Argument { identifier: 't', description: String::from("Prints \"Rust is the best language\"") };
        let mut arg_array: Vec<Argument> = Vec::new();
        arg_array.push(arg1);
        arg_array.push(arg2);
        let ev = argumentparser::parse::parseargs(args, arg_array);
        println!("{:?}", ev);
    }

    #[test]
    fn pstring() {
        let mut pstr = PString::from_bytes(vec![104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104]);
        println!("{}", "----");
        println!("{:?}", pstr.compression);
        pstr.compress();
        println!("{:?}", pstr.as_bytes());
        println!("{:?}", pstr.as_bytes_raw());
        println!("{}", pstr);
    }

    #[test]
    fn average() {
        let average = num::average(vec![4_f64, 2_f64]);
        println!("{:?}", average);
    }

    #[bench]
    /// Create 256 compressed PStrings from a 256 byte string and then decompress them.
    fn bench_pstring(b: &mut Bencher) {
        let teststr = "aOYtemeapoDKOJNEzQrdfSozFSqsetcYHpM6kurYRzbnZ99AWpQoCDwuH6pvsjJZsgsMXSWYAB4XEiF6HmiRhiNuawKduBo9VSFt02Ub92tfZjKsTWON7wZQhFxapBWsf3hmD6M7BSc3Xw63zVO0tfRti0fWjluMNWgbNsmjm9ql5R9Eq1jpNUIOO9k1I2OwTrngKLA8Ieqn0U9Jith3kERmJMkjdKoGSGNueRtlqOM1e4lnYBdbnwEpy9Qbq46S";

        b.iter(|| {
            for _ in 1..256 {
                let mut pstr = PString::from_str(teststr);
                pstr.compress();
                let mut mv = pstr.as_bytes();
                mv.push(104);
            }
        });
    }
}