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
use crate::cmd::{Parse, ParseError}; use crate::{Connection, Db, Frame}; use bytes::Bytes; use std::time::Duration; use tracing::{debug, instrument}; /// Set `key` to hold the string `value`. /// /// If `key` already holds a value, it is overwritten, regardless of its type. /// Any previous time to live associated with the key is discarded on successful /// SET operation. /// /// # Options /// /// Currently, the following options are supported: /// /// * EX `seconds` -- Set the specified expire time, in seconds. /// * PX `milliseconds` -- Set the specified expire time, in milliseconds. #[derive(Debug)] pub struct Set { /// the lookup key key: String, /// the value to be stored value: Bytes, /// When to expire the key expire: Option<Duration>, } impl Set { /// Create a new `Set` command which sets `key` to `value`. /// /// If `expire` is `Some`, the value should expire after the specified /// duration. pub fn new(key: impl ToString, value: Bytes, expire: Option<Duration>) -> Set { Set { key: key.to_string(), value, expire, } } /// Get the key pub fn key(&self) -> &str { &self.key } /// Get the value pub fn value(&self) -> &Bytes { &self.value } /// Get the expire pub fn expire(&self) -> Option<Duration> { self.expire } /// Parse a `Set` instance from a received frame. /// /// The `Parse` argument provides a cursor-like API to read fields from the /// `Frame`. At this point, the entire frame has already been received from /// the socket. /// /// The `SET` string has already been consumed. /// /// # Returns /// /// Returns the `Set` value on success. If the frame is malformed, `Err` is /// returned. /// /// # Format /// /// Expects an array frame containing at least 3 entries. /// /// ```text /// SET key value [EX seconds|PX milliseconds] /// ``` pub(crate) fn parse_frames(parse: &mut Parse) -> crate::Result<Set> { use ParseError::EndOfStream; // Read the key to set. This is a required field let key = parse.next_string()?; // Read the value to set. This is a required field. let value = parse.next_bytes()?; // The expiration is optional. If nothing else follows, then it is // `None`. let mut expire = None; // Attempt to parse another string. match parse.next_string() { Ok(s) if s == "EX" => { // An expiration is specified in seconds. The next value is an // integer. let secs = parse.next_int()?; expire = Some(Duration::from_secs(secs)); } Ok(s) if s == "PX" => { // An expiration is specified in milliseconds. The next value is // an integer. let ms = parse.next_int()?; expire = Some(Duration::from_millis(ms)); } // Currently, mini-redis does not support any of the other SET // options. An error here results in the connection being // terminated. Other connections will continue to operate normally. Ok(_) => return Err("currently `SET` only supports the expiration option".into()), // The `EndOfStream` error indicates there is no further data to // parse. In this case, it is a normal run time situation and // indicates there are no specified `SET` options. Err(EndOfStream) => {} // All other errors are bubbled up, resulting in the connection // being terminated. Err(err) => return Err(err.into()), } Ok(Set { key, value, expire }) } /// Apply the `Set` command to the specified `Db` instance. /// /// The response is written to `dst`. This is called by the server in order /// to execute a received command. #[instrument(skip(self, db, dst))] pub(crate) async fn apply(self, db: &Db, dst: &mut Connection) -> crate::Result<()> { // Set the value in the shared database state. db.set(self.key, self.value, self.expire); // Create a success response and write it to `dst`. let response = Frame::Simple("OK".to_string()); debug!(?response); dst.write_frame(&response).await?; Ok(()) } /// Converts the command into an equivalent `Frame`. /// /// This is called by the client when encoding a `Set` command to send to /// the server. pub(crate) fn into_frame(self) -> Frame { let mut frame = Frame::array(); frame.push_bulk(Bytes::from("set".as_bytes())); frame.push_bulk(Bytes::from(self.key.into_bytes())); frame.push_bulk(self.value); frame } }