diff --git a/subjects/borrow_box/README.md b/subjects/borrow_box/README.md index c452202a0b..d4a58fb862 100644 --- a/subjects/borrow_box/README.md +++ b/subjects/borrow_box/README.md @@ -6,13 +6,13 @@ Game time. You will implement some **CRUD** functionality for a game session. You will need to implement the `GameSession` structure with the following associated functions: -- `new`: which initializes a game session state with player names and some other information. This function returns the structure wrapped in a `Box`. +- `new`: which initializes a game session state with player names and some other information. -- `read_winner`: which returns a tuple with the name and score of the player who is currently winning. In the case that no player is winning, it should return the same tuple with the string `"Same score! tied"` and the tied score. +- `read_winner`: which returns a tuple referencing the player who is currently winning. In the case that no player is winning, it should return `None`. - `update_score`: which receives the name of a player, and increments their score. This function should **do nothing** if the the game session is already finished or if the name received doesn't match any player. -- `delete`: which takes ownership of the boxed game session and returns a string: `"game deleted: id -> 0"`, where `0` is the id of the `GameSession`. +- `delete`: which takes ownership of the game session and returns a string: `"game deleted: id -> {id}"`, where `{id}` is the id of the `GameSession`. > Examples for `nb_games`: > @@ -25,23 +25,26 @@ You will implement some **CRUD** functionality for a game session. You will need #[derive(Debug, Clone, Eq, PartialEq)] pub struct GameSession { pub id: u32, - pub p1: (String, u16), - pub p2: (String, u16), - pub nb_games: u16 + pub p1: (String, u32), + pub p2: (String, u32), + pub nb_games: u32, } impl GameSession { - pub fn new(id: u32, p1_name: String, p2_name: String, nb_games: u16) -> Box { - + pub fn new(id: u32, p1_name: String, p2_name: String, nb_games: u32) -> GameSession { + todo!() } - pub fn read_winner(&self) -> (String, u16) { + pub fn read_winner(&self) -> Option<&(String, u32)> { + todo!() } - pub fn update_score(&mut self, user_name: String) { + pub fn update_score(&mut self, user_name: &str) { + todo!() } - pub fn delete(self) -> String { + pub fn delete(self) -> String { + todo!() } } ``` @@ -56,28 +59,23 @@ use borrow_box::*; fn main() { let mut game = GameSession::new(0, String::from("Joao"), String::from("Susana"), 5); println!("{:?}", game.read_winner()); - // output : ("Same score! tied", 0) - game.update_score(String::from("Joao")); - game.update_score(String::from("Joao")); - game.update_score(String::from("Susana")); - game.update_score(String::from("Susana")); + game.update_score("Joao"); + game.update_score("Joao"); + game.update_score("Susana"); + game.update_score("Susana"); println!("{:?}", game.read_winner()); - // output : ("Same score! tied", 2) - game.update_score(String::from("Joao")); - // this one will not count because it already 5 games played, the nb_games - game.update_score(String::from("Susana")); + game.update_score("Joao"); + // This one will not count because it already 5 games played, the `nb_games` + game.update_score("Susana"); println!("{:?}", game.read_winner()); - // output : ("Joao", 3) println!("{:?}", game.delete()); - // output : "game deleted: id -> 0" // game.read_winner(); - // this will give error - // because the game was dropped, no longer exists on the heap + // This will give an error as the game was dropped with `delete` and no longer exists } ``` @@ -85,9 +83,9 @@ And its output: ```console $ cargo run -("Same score! tied", 0) -("Same score! tied", 2) -("Joao", 3) +None +None +Some(("Joao", 3)) "game deleted: id -> 0" $ ``` diff --git a/subjects/bowling_score/README.md b/subjects/bowling_score/README.md index ecb6ffe39d..70820a9572 100644 --- a/subjects/bowling_score/README.md +++ b/subjects/bowling_score/README.md @@ -43,21 +43,19 @@ pub enum Error { GameComplete, } -pub struct BowlingGame { - ... -} +pub struct BowlingGame {} impl BowlingGame { pub fn new() -> Self { - unimplemented!(); + todo!(); } - pub fn roll(&mut self, pins: u16) -> Result<(), Error> { - unimplemented!(); + pub fn roll(&mut self, pins: u32) -> Result<(), Error> { + todo!(); } - pub fn score(&mut self) -> Some(u16) { - unimplemented!(); + pub fn score(&self) -> Option { + todo!(); } } ``` diff --git a/subjects/box_it/README.md b/subjects/box_it/README.md index 05070ea34e..12881da7f8 100644 --- a/subjects/box_it/README.md +++ b/subjects/box_it/README.md @@ -4,18 +4,19 @@ Create the following **functions**: -- `transform_and_save_on_heap`: which accepts a string of numbers separated by spaces. If a number has a `'k'` as a suffix it should be multiplied by 1000. The function transforms those numbers into a vector of `u32`, and saves them in the heap using `Box`. +- `parse_into_boxed`: which accepts a string of numbers separated by spaces. If a number has a `k` as a suffix it should be multiplied by 1000. The function parses these numbers and boxes them into a vector of `Box`. -- `take_value_ownership`: which accepts the return value from `transform_and_save_on_heap`, unboxes the value, and returns it. +- `into_unboxed`: which accepts the value returned from `parse_into_boxed` and unboxes each element into another vector. ### Expected Functions ```rust -pub fn transform_and_save_on_heap(s: String) -> Box> { - +pub fn parse_into_boxed(s: String) -> Vec> { + todo!() } -pub fn take_value_ownership(a: Box>) -> Vec { +pub fn into_unboxed(a: Vec>) -> Vec { + todo!() } ``` @@ -24,20 +25,22 @@ pub fn take_value_ownership(a: Box>) -> Vec { Here is a program to test your functions ```rust +use std::mem; + use box_it::*; fn main() { - let new_str = String::from("5.5k 8.9k 32"); + let s = "5.5k 8.9k 32".to_owned(); + + let boxed = parse_into_boxed(s); + println!("Element value: {:?}", boxed[0]); + println!("Element size: {:?} bytes", mem::size_of_val(&boxed[0])); - // creating a variable and we save it in the Heap - let a_h = transform_and_save_on_heap(new_str); - println!("Box value : {:?}", &a_h); - println!("size occupied in the stack : {:?} bytes", (std::mem::size_of_val(&a_h))); + let unboxed = into_unboxed(boxed); + println!("Element value: {:?}", unboxed[0]); + println!("Element size: {:?} bytes", mem::size_of_val(&unboxed[0])); - let a_b_v = take_value_ownership(a_h); - println!("value : {:?}", &a_b_v); - println!("size occupied in the stack : {:?} bytes", (std::mem::size_of_val(&a_b_v))); - // whenever the box, in this case "a_h", goes out of scope it will be deallocated, freed + // As with everything related to regular Rust memory management, both the `Vec` and the `Box`es will be properly dropped when out of scope and freed, ensuring no leaks } ``` @@ -45,10 +48,10 @@ And its output: ```console $ cargo run -Box value : [5500, 8900, 32] -size occupied in the stack : 8 bytes -value : [5500, 8900, 32] -size occupied in the stack : 24 bytes +Element value: 5500 +Element size: 8 bytes +Element value: 5500 +Element size: 4 bytes $ ``` diff --git a/subjects/box_recursion/README.md b/subjects/box_recursion/README.md index e5b15258d7..e5714caea2 100644 --- a/subjects/box_recursion/README.md +++ b/subjects/box_recursion/README.md @@ -5,21 +5,32 @@ Using the given code, create the following **associated functions**: - `new`: which will initialize the `WorkEnvironment` with `grade` set to `None`. -- `add_worker`: which receives two strings, one being the role and the other the name of the worker. It will add the worker at the start of the list. -- `remove_worker`: which removes the last worker that was placed in the `WorkEnvironment`, this function returns an `Option` with the name of the worker. +- `add_worker`: which receives a name of a worker and a role. It will add the worker at the start of the list. +- `remove_worker`: which removes the last worker that was placed in the `WorkEnvironment`, this function returns an `Option` with the name of the removed worker. - `last_worker`: which returns an `Option` with a tuple containing the name and role of the last added worker. -You must also create a type named `Link`. This will be the connection between the `WorkEnvironment` and `Worker` structures. This will be a recursion type, and it must point to `None` if there is no `Worker` to point to. +You must also create a `Role` enum which can be `CEO`, `Manager`, or `Worker`, and will represent the role of a worker. This enum should implement the `From` trait for `&str`. + +Additionally, create a type named `Link`, which will be the connection between the `WorkEnvironment` and `Worker` structures. It will point to `None` if there is no `Worker` to point to. ### Expected Functions and structures ```rust +#[derive(Debug, PartialEq)] +pub enum Role { + CEO, + Manager, + Worker, +} + +impl From<&str> for Role {} + #[derive(Debug)] pub struct WorkEnvironment { pub grade: Link, } -pub type Link = +pub type Link; #[derive(Debug)] pub struct Worker { @@ -29,10 +40,21 @@ pub struct Worker { } impl WorkEnvironment { - pub fn new() -> WorkEnvironment {} - pub fn add_worker(&mut self, role: String, name: String) {} - pub fn remove_worker(&mut self) -> Option {} - pub fn last_worker(&self) -> Option<(String, String)> {} + pub fn new() -> Self { + todo!() + } + + pub fn add_worker(&mut self, name: &str, role: &str) { + todo!() + } + + pub fn remove_worker(&mut self) -> Option { + todo!() + } + + pub fn last_worker(&self) -> Option<(String, Role)> { + todo!() + } } ``` @@ -45,10 +67,12 @@ use box_recursion::*; fn main() { let mut list = WorkEnvironment::new(); - list.add_worker(String::from("CEO"), String::from("Marie")); - list.add_worker(String::from("Manager"), String::from("Monica")); - list.add_worker(String::from("Normal Worker"), String::from("Ana")); - list.add_worker(String::from("Normal Worker"), String::from("Alice")); + + list.add_worker("Marie", "CEO"); + list.add_worker("Monica", "Manager"); + list.add_worker("Ana", "Normal Worker"); + list.add_worker("Alice", "Normal Worker"); + println!("{:#?}", list); println!("{:?}", list.last_worker()); @@ -56,8 +80,11 @@ fn main() { list.remove_worker(); list.remove_worker(); list.remove_worker(); + println!("{:?}", list); + list.remove_worker(); + println!("{:?}", list); } ``` @@ -69,19 +96,19 @@ $ cargo run WorkEnvironment { grade: Some( Worker { - role: "Normal Worker", + role: Worker, name: "Alice", next: Some( Worker { - role: "Normal Worker", + role: Worker, name: "Ana", next: Some( Worker { - role: "Manager", + role: Manager, name: "Monica", next: Some( Worker { - role: "CEO", + role: CEO, name: "Marie", next: None, }, @@ -93,8 +120,8 @@ WorkEnvironment { }, ), } -Some(("Alice", "Normal Worker")) -WorkEnvironment { grade: Some(Worker { role: "CEO", name: "Marie", next: None }) } +Some(("Alice", Worker)) +WorkEnvironment { grade: Some(Worker { role: CEO, name: "Marie", next: None }) } WorkEnvironment { grade: None } $ ``` diff --git a/subjects/drop_the_thread/README.md b/subjects/drop_the_thread/README.md index d78c1c6d97..fbd4c5ab8b 100644 --- a/subjects/drop_the_thread/README.md +++ b/subjects/drop_the_thread/README.md @@ -2,64 +2,85 @@ ### Instructions -> Interior mutability is a design pattern in Rust that allows you to mutate data even when there are immutable references to that data. +> Interior mutability is a design pattern in Rust that allows you to mutate data even with immutable references to that data. In this exercise, you will create a Drop Checker API. Define the following structures: +- `ThreadPool` containing: + - `drops`: will save the number of dropped threads. + - `states`: will save the state of multiple threads. If the thread is not dropped, the state will be `false`, and will be `true` otherwise. -- `Workers`: containing: - - `drops`: that will save the number of dropped threads. - - `states`: that will save the state of multiple threads. If the thread is not dropped, the state will be `false`, and will be `true` otherwise. -- `Thread`: containing: +- `Thread` containing: - `pid`: the id of the thread. - `cmd`: the name of the thread. - - `parent`: a link to the structure `Workers`. (Tip: this should be a reference). + - `parent`: a reference to the structure `ThreadPool`. -You'll need to also add the following associated functions to the structures: +You'll also need to add the following associated functions to the structures: -- `Workers` : - - `new`: that creates a default worker. - - `new_worker`: that returns a tuple with the `pid` and a new `Thread`. This function must receive a `String` representing the `cmd`. - - `is_dropped`: that receives a `pid` and returns a `bool` that indicates the state of the thread. - - `track_worker`: which returns a `usize` representing the length of the `states` vector. (The index of the next new thread). - - `add_drop`: which is **called by the `Drop` trait**. It will receive a `pid` that will be used to change the state of the thread. If the state of that thread is `true` then it will panic with the message `"X is already dropped"`, where `X` represents the `pid`). Otherwise it should change the state to `true` and increment the `drops` field by 1. +- `ThreadPool` : + - `new`: initializes the structure with default values. + - `new_thread`: creates a new thread and mutates own state accordingly. Returns a tuple with the `pid` and a new `Thread`. + - `is_dropped`: that receives a `pid` and returns whether the thread was already dropped. + - `thread_len`: returns the length of the `states` vector. (The index of the next new thread). + - `drop_thread`: this **should be called when dropping a `Thread`**. It will receive a `pid` that will be used to change the state of the thread. If the thread was already dropped then it will panic with the message `"X is already dropped"`, where `X` represents the `pid`. Otherwise it should change the state to `true` and increment the `drops` field by 1. - `Thread`: - - `new_thread`: that initializes a new thread. - - `skill`: that drops the thread. + - `new`: initializes a new thread. + - `skill`: function that drops the thread. - You must implement the `Drop` trait for the `Thread` structure. In this trait you must call the function `add_drop` so that the state of the thread changes. ### Expected Functions ```rust -use std::cell::{RefCell, Cell}; +use std::cell::{Cell, RefCell}; -#[derive(Debug, Default, Clone, Eq, PartialEq)] -pub struct Workers { +#[derive(Debug)] +pub struct ThreadPool { pub drops: Cell, pub states: RefCell> } -impl Workers { - pub fn new() -> Workers {} - pub fn new_worker(&self, c: String) -> (usize, Thread) {} - pub fn track_worker(&self) -> usize {} - pub fn is_dropped(&self, id: usize) -> bool {} - pub fn add_drop(&self, id: usize) {} +impl ThreadPool { + pub fn new() -> Self { + todo!() + } + + pub fn new_thread(&self, c: String) -> (usize, Thread) { + todo!() + } + + pub fn thread_len(&self) -> usize { + todo!() + } + + pub fn is_dropped(&self, id: usize) -> bool { + todo!() + } + + pub fn drop_thread(&self, id: usize) { + todo!() + } } -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct Thread<'a> { +#[derive(Debug)] +pub struct Thread { // expected public fields } impl<'a> Thread<'a> { - pub fn new_thread(p: usize, c: String, t: &'a Workers) -> Thread {} - pub fn skill(self) {} + pub fn new(p: usize, c: String, t: &'a ThreadPool) -> Self { + todo!() + } + + pub fn skill(self) { + todo!() + } } + +impl Drop for Thread<'_> {} ``` ### Usage @@ -71,24 +92,24 @@ use std::rc::Rc; use drop_the_thread::*; fn main() { - let worker = Workers::new(); - let (id, thread) = worker.new_worker(String::from("command")); - let (id1, thread1) = worker.new_worker(String::from("command1")); + let pool = ThreadPool::new(); + let (id, thread) = pool.new_thread(String::from("command")); + let (id1, thread1) = pool.new_thread(String::from("command1")); thread.skill(); - println!("{:?}", (worker.is_dropped(id), id, &worker.drops)); + println!("{:?}", (pool.is_dropped(id), id, &pool.drops)); thread1.skill(); - println!("{:?}", (worker.is_dropped(id1), id1, &worker.drops)); + println!("{:?}", (pool.is_dropped(id1), id1, &pool.drops)); - let (id2, thread2) = worker.new_worker(String::from("command2")); + let (id2, thread2) = pool.new_thread(String::from("command2")); let thread2 = Rc::new(thread2); let thread2_clone = thread2.clone(); drop(thread2_clone); - println!("{:?}", (worker.is_dropped(id2), id2, &worker.drops, Rc::strong_count(&thread2))); + println!("{:?}", (pool.is_dropped(id2), id2, &pool.drops, Rc::strong_count(&thread2))); } ``` @@ -104,6 +125,6 @@ $ ### Notions -- [Trait std::ops::Drop](https://doc.bccnsoft.com/docs/rust-1.36.0-docs-html/std/ops/trait.Drop.html) +- [Trait std::ops::Drop](https://doc.rust-lang.org/std/ops/trait.Drop.html) - [Struct std::cell::RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html) - [Interior Mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) diff --git a/subjects/how_many_references/README.md b/subjects/how_many_references/README.md index d1dbd3bdd4..62ca5d2c18 100644 --- a/subjects/how_many_references/README.md +++ b/subjects/how_many_references/README.md @@ -2,30 +2,40 @@ ### Instructions -Create the following **functions**: +Create a structure `Node` which will contain a vector of `Rc`s with the following **methods**: -- `add_element`: which adds an element to the list in the `Node`. -- `how_many_references`: which returns how many times the value is referenced in the code. +- `new`: which creates a new `Node` with the given initial state. +- `add_element`: which adds an element to the `ref_list`. - `rm_all_ref`: which accepts an `Rc` and removes all elements from the vector that are equal to that value. This should only happen if the two `Rc`s point to the same allocation. +Additionally, create the **function** `how_many_references`, which returns how many clones of the same `Rc` passed as argument exist. + ### Expected Functions and structures ```rust -pub use std::rc::Rc; +use std::rc::Rc; pub struct Node { pub ref_list: Vec>, } impl Node { - pub fn new(ref_list: Vec>) -> Node { - Node { ref_list: ref_list } + pub fn new(ref_list: Vec>) -> Self { + todo!() + } + + pub fn add_element(&mut self, element: Rc) { + todo!() + } + + pub fn rm_all_ref(&mut self, element: Rc) { + todo!() } - pub fn add_element(&mut self, element: Rc) {} - pub fn rm_all_ref(&mut self, element: Rc) {} } -pub fn how_many_references(ref_list: &Rc) -> usize {} +pub fn how_many_references(ref_list: &Rc) -> usize { + todo!() +} ``` ### Usage @@ -36,11 +46,11 @@ Here is a program to test your functions, use how_many_references::*; fn main() { - let a = Rc::new(String::from("a")); - let b = Rc::new(String::from("b")); - let c = Rc::new(String::from("c")); + let a = Rc::new("a".to_owned()); + let b = Rc::new("b".to_owned()); + let c = Rc::new("c".to_owned()); - let a1 = Rc::new(String::from("a")); + let a1 = Rc::new("a".to_owned()); let mut new_node = Node::new(vec![a.clone()]); new_node.add_element(b.clone()); diff --git a/subjects/ref_cell/README.md b/subjects/ref_cell/README.md index 5dd1a048da..95200fec73 100644 --- a/subjects/ref_cell/README.md +++ b/subjects/ref_cell/README.md @@ -2,97 +2,66 @@ ### Instructions -#### First part (messenger.rs) +Create a `Tracker` structure with the following fields: -Create a module named `messenger`. This module will be able to inform a user of how many references of a given value they are using. -The main objective of this module is to limit how many times a value is referenced. - -For this module the following must be created: - -Implement `Logger`: a trait which implements the following three functions: - -```rust -fn warning(&self, msg: &str); -fn info(&self, msg: &str); -fn error(&self, msg: &str); -``` - -Implement the `Tracker` structure with the following fields: - -- `logger`: a reference to `Logger`. +- `messages`: a **public** vector of all the sent messages. - `value`: the count of how many times the value was referenced. It should not exceed `max`. -- `max`: the max count of references. +- `max`: the maximum count of references. -Add the following associated functions to `Tracker`: +Add the following methods to `Tracker`: -- `new`: that initializes the structure. -- `set_value`: that sets the `value`. It should compare the number of references to `value` and `max` to work out the percentage used. It should write to the following traits if it exceeds the specified usage percentage: - - percentage >= 100%: `"Error: you are over your quota!"` should be written to `error`. - - percentage >= 70% and percentage < 100%: `"Warning: you have used up over X% of your quota! Proceeds with precaution"` should be written to `warning`, where `X` should be replaced with the calculated percentage. -- `peek`: that will take a peek at how much usage the variable already has. It should write `"Info: you are using up to X% of your quota"` to the `info` trait function. `X` should be replaced with the calculated percentage. +- `new`: that initializes the structure with a passed `max` value. +- `set_value`: that sets the structure's `value` to the passed argument `value`'s reference count only if it isn't higher than `max`. Should it exceed `max` it should write to `messages` the message `"Error: You can't go over your quota!"`. Should it not exceed `max` but exceed the percentage of 70%, it should write to `messages` the message `"Warning: You have used up over X% of your quota!"`, where `X` should be replaced with the calculated percentage. -### Second part (lib.rs) - -Now that you've created `messenger`, you can now create the following: - -Create the `Worker` structure with the following fields: - -- `track_value`: which is the value that will be tracked by the tracker. -- `mapped_messages`: that will store the latest messages from the `Logger` trait functions. This will be a HashMap. The key will represent the type of message (`info`, `error` or `warning`), and the value will be the actual message. -- `all_messages`: that will be a vector of **all** messages sent. - -Create the following associated functions for `Worker`: - -- `new`: that initializes a `Worker` structure. -- `Logger`: to use the trait `Logger`, you must implement it for the `Worker` structure. Each function (`warning`, `error` and `info`) must insert the message to the respective field of the `Worker` structure. - -You must use **interior mutability**, this means it must be possible to mutate data, even when there are immutable references to that data. Consequently, the user will not need to use the keyword `mut`. _tip:_ RefCell. +- `peek`: that will take a peek at how much usage the passed argument `value` already has compared to the structure's `max`. It should write into `messages` the string `"Info: This value would use X% of your quota"`. `X` should be replaced with the calculated percentage. ### Usage Here is a program to test your function, ```rust +use std::rc::Rc; + use ref_cell::*; fn main() { - // initialize the worker - let logger = Worker::new(1); + let v = Rc::new(1); // we have one reference to this Rc // initialize the tracker, with the max number of // called references as 10 - let track = Tracker::new(&logger, 10); + let track = Tracker::new(10); - let _a = logger.track_value.clone(); // |\ - let _a1 = logger.track_value.clone(); // | -> increase the Rc to 4 references - let _a2 = logger.track_value.clone(); // |/ + let _v = Rc::clone(&v); // |\ + let _v = Rc::clone(&v); // | -> increase the Rc to 4 references + let _v = Rc::clone(&v); // |/ // take a peek of how much we already used from our quota - track.peek(&logger.track_value); + track.peek(&v); - let _b = logger.track_value.clone(); // |\ - let _b1 = logger.track_value.clone(); // | -> increase the Rc to 8 references - let _b2 = logger.track_value.clone(); // | / - let _b3 = logger.track_value.clone(); // |/ + let _v = Rc::clone(&v); // |\ + let _v = Rc::clone(&v); // | -> increase the Rc to 8 references + let _v = Rc::clone(&v); // | / + let _v = Rc::clone(&v); // |/ - // this will set the value and making a verification of - // how much we already used of our quota - track.set_value(&logger.track_value); + // this will change the tracker's inner value + // and make a verification of how much we already used of our quota + track.set_value(&v); - let _c = logger.track_value.clone(); // | -> increase the Rc to 9 references + let _v = Rc::clone(&v); // increase the Rc to 9 references + let _v = Rc::clone(&v); // increase the Rc to 10 references, the maximum we allow - // this will set the value and making a verification of - // how much we already used of our quota - track.set_value(&logger.track_value); + track.set_value(&v); - let _c1 = logger.track_value.clone(); // | -> increase the Rc to 10 references, this will be the limit + let _v = Rc::clone(&v); // surpass the maximum allowed references - track.set_value(&logger.track_value); + track.peek(&v); + track.set_value(&v); - for (k ,v) in logger.mapped_messages.into_inner() { - println!("{:?}", (k ,v)); - } - println!("{:?}", logger.all_messages.into_inner()); + track + .messages + .borrow() + .iter() + .for_each(|msg| println!("{}", msg)); } ``` @@ -100,15 +69,11 @@ And its output: ```console $ cargo run -("Info", "you are using up to 40% of your quota") -("Warning", "you have used up over 90% of your quota! Proceeds with precaution") -("Error", "you are over your quota!") -[ - "Info: you are using up to 40% of your quota", - "Warning: you have used up over 80% of your quota! Proceeds with precaution", - "Warning: you have used up over 90% of your quota! Proceeds with precaution", - "Error: you are over your quota!" -] +Info: This value would use 40% of your quota +Warning: You have used up over 80% of your quota! +Warning: You have used up over 100% of your quota! +Info: This value would use 110% of your quota +Error: You can't go over your quota! $ ```