1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
#![crate_name = "idioma"] #![crate_type = "lib"] //! As Rust developers we deeply care about safety and error handling - our programs are fast and //! reliable. However, users never make it easy for us: they misunderstand instructions and break //! things. When we catch them doing something they shouldn't be doing, we let them know (usually) //! with an error message. //! //! Every command line tool prints handy messages to `stdout` from time to time, and to do so, //! requires a function or two. I noticed that whenever I start a new project I tend to copy the //! `util.rs` that contains those display functions from my last project. That is simply no good. //! //! It means that my error messages //! - differ in style (since I regularly alter code in that util file); //! - don't look like idiomatic Rust messages; //! - require that `COPY + PASTE` operation for every new project. //! //! And I strongly believe that I am not alone in this. Take a look at [this code][1] by //! [brain-lang]: //! //! ``` //! macro_rules! exit_with_error( //! ($($arg:tt)*) => { { //! use std::process; //! eprintln!($($arg)*); //! process::exit(1); //! } } //! ); //! ``` //! //! [1]: https://github.com/brain-lang/brainfuck/blob/master/src/bin/brainfuck.rs#L21 //! [brain-lang]: https://github.com/brain-lang/ //! //! As you can see, they wrote this macro right next to the `main` function and it is the same //! problem that I have with my util file. //! //! The `idioma` library solves all these problems forever. Here's how. //! //! In your `Cargo.toml` file. //! //! ```toml //! [dependencies] //! idioma = "*" //! ``` //! //! Include in any Rust file. //! //! ``` //! extern crate idioma; //! ``` //! //! Use within a function. //! //! ``` //! use idioma::*; //! fn foo(i: i32) { //! if i != 42 { //! error("Your taste is appalling.").exit(1); //! } //! } //! ``` extern crate colored; use colored::*; use std::{ fmt::{self, Display}, process, }; /// `Text` is the main type that gets thrown around between functions and methods. It is basically a /// `String`, but it had to be made into a separate `struct` so that it would be possible to `impl` /// some things for it. #[derive(Debug)] pub struct Text { text: String, } /// `Error` type is an alias of the `Text` type that we use to denote errors specifically. The fact /// that this is an alias also means that it has access to all the methods that `Text` has. pub type Error = Text; impl std::fmt::Display for Text { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.text) } } impl Text { /// Returns `Text` by constructing it from a given `label` and `message`. /// /// # Examples /// /// ``` /// use idioma::*; /// use colored::*; /// Text::make("lol".cyan().bold(), "LMAO you're so funny."); /// ``` pub fn make<I>(label: ColoredString, message: I) -> Self where I: Display, { Self { text: format!("{}{} {}", label, ":".bold(), message), } } /// Displays `Text` thanks to the `std::fmt::Display` trait. /// /// # Examples /// /// ``` /// use idioma::*; /// warning("This message is going to be printed out immediately!").print(); /// ``` pub fn print(&self) { println!("{}", self) } /// Displays `message` and terminates the program via `std::process::exit`. Please note that /// this function returns `Text` back in case we need to please the type checker. See /// `exit_if_error` function for an example of that. /// /// # Examples /// /// ``` /// use idioma::*; /// error("You were not supposed to mess with me!").exit(1); /// ``` /// /// You can even combine `custom` and `exit` to produce some real nice stuff. /// /// ``` /// use idioma::*; /// use colored::*; /// custom("lol".cyan().bold())("Did you expect something serious here? LMAO XD").exit(1); /// ``` #[allow(unreachable_code)] pub fn exit(self, code: i32) -> Self { self.print(); process::exit(code); self } } /// Allows you to create and print messages with custom labels. Essentially, allows you to write /// your own functions like `error`, `info`, etc. that we already have here. /// /// # Example /// /// ``` /// use idioma::*; /// use colored::*; /// let custom_label = custom("custom".blue().bold()); /// custom_label("This is a custom label. You can make one too!"); /// custom_label("Declare it once and reuse."); /// ``` pub fn custom<I>(label: ColoredString) -> impl Fn(I) -> Text where I: Display, { move |message| Text::make(label.clone(), message) } /// Returns a green and shiny success message. /// /// # Example /// /// ``` /// use idioma::*; /// success("A man of honour must always strive for a greater success in life."); /// ``` pub fn success<I>(message: I) -> Text where I: Display, { Text::make("success".green().bold(), message) } /// Displays a warning. /// /// # Example /// /// ``` /// use idioma::*; /// warning("Very soon, you will run out of water."); /// ``` pub fn warning<I>(message: I) -> Text where I: Display, { Text::make("warning".yellow().bold(), message) } /// Returns a neutral info message. /// /// # Example /// /// ``` /// use idioma::*; /// info("I came here to write some code and kiss some pretty ladies and, as you can see, \ /// I'm done with the code."); /// ``` pub fn info<I>(message: I) -> Text where I: Display, { Text::make("info".purple().bold(), message) } /// Returns a bright-red error message that draws attention. /// /// # Example /// /// ``` /// use idioma::*; /// error("You were not supposed to mess with me!"); /// ``` pub fn error<I>(message: I) -> Error where I: Display, { Text::make("error".red().bold(), message) } pub fn into<O, E>(result: Result<O, E>) -> Result<O, Error> where E: Display, { match result { Ok(o) => Ok(o), Err(e) => Err(error(e)), } } pub fn exit_if_error<O>(result: Result<O, Error>) -> Result<O, Error> { match result { Ok(o) => Ok(o), Err(e) => Err(e.exit(1)), } }