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
use std::hash::Hash;
use std::time::Duration;

use crate::cache::key_description::KeyDescription;
use crate::cache::types::{KeyId, Weight};

pub mod acknowledgement;
pub mod error;
pub mod command_executor;

/// CommandType defines various write commands including:
/// Put             : attempts to put the new key/value pair in the cache
/// PutWithTTL      : attempts to put the new key/value pair with time_to_live in the cache
/// Delete          : attempts to delete the key
/// UpdateWeight    : updates the weight of the key. This command is sent as a part of `put_or_update` operation
/// Shutdown        : informs the `crate::cache::command::command_executor::CommandExecutor` that the cache is being shutdown
pub(crate) enum CommandType<Key, Value>
    where Key: Hash + Eq + Clone {
    Put(KeyDescription<Key>, Value),
    PutWithTTL(KeyDescription<Key>, Value, Duration),
    Delete(Key),
    UpdateWeight(KeyId, Weight),
    Shutdown,
}

/// Provides the description of each command
/// `description` is used if there is an error in sending a command to the `crate::cache::command::command_executor::CommandExecutor`
impl<Key, Value> CommandType<Key, Value>
    where Key: Hash + Eq + Clone {
    fn description(&self) -> String {
        match self {
            CommandType::Put(_, _) => "Put".to_string(),
            CommandType::PutWithTTL(_, _, _) => "PutWithTTL".to_string(),
            CommandType::Delete(_) => "Delete".to_string(),
            CommandType::UpdateWeight(_, _) => "UpdateWeight".to_string(),
            CommandType::Shutdown => "Shutdown".to_string(),
        }
    }
}

/// CommandStatus defines the status of each command.
///
/// `Pending`:        the initial status of the command, before a command is acted upon.
///
/// `Accepted`:       the command is successfully completed.
///
/// `Rejected`:       the command is rejected. `RejectionReason` is now available with the `Rejected` status (v0.0.2)
    /// - `Put` may be rejected for various reasons. One reason is: the weight of the the incoming key/value pair is more than the total cache weight.
    /// - `Delete` will be rejected if the key to be deleted is not preset in the cache.
///
/// `ShuttingDown`:   all the commands that could sneak in while the cache is being shutdown will be returned with `ShuttingDown` status.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CommandStatus {
    Pending,
    Accepted,
    Rejected(RejectionReason),
    ShuttingDown,
}

/// RejectionReason defines the reason for a command getting rejected. Available since v0.0.2.
///
/// `EnoughSpaceIsNotAvailableAndKeyFailedToEvictOthers`: If the cache weight is full, and the incoming key can not evict others.
///
/// `KeyWeightIsGreaterThanCacheWeight`: The weight of the incoming key is greater than the total cache weight.
///
/// `KeyDoesNotExist`: Key does not exist during delete operation.
///
/// `KeyAlreadyExists`: Key already exists during put operation.
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RejectionReason {
    EnoughSpaceIsNotAvailableAndKeyFailedToEvictOthers,
    KeyWeightIsGreaterThanCacheWeight,
    KeyDoesNotExist,
    KeyAlreadyExists,
}

#[cfg(test)]
mod tests {
    use std::time::Duration;

    use crate::cache::command::CommandType;
    use crate::cache::key_description::KeyDescription;

    #[test]
    fn command_description_put() {
        let put = CommandType::Put(
            KeyDescription::new(
                "topic", 1, 2090, 10,
            ),
            "microservices");

        assert_eq!("Put", put.description());
    }

    #[test]
    fn command_description_put_with_ttl() {
        let put = CommandType::PutWithTTL(
            KeyDescription::new(
                "topic", 1, 2090, 10,
            ),
            "microservices",
            Duration::from_millis(10),
        );

        assert_eq!("PutWithTTL", put.description());
    }

    #[test]
    fn command_description_delete() {
        let delete: CommandType<&str, &str> = CommandType::Delete("topic");

        assert_eq!("Delete", delete.description());
    }

    #[test]
    fn command_description_update_weight() {
        let update_weight: CommandType<&str, &str> = CommandType::UpdateWeight(10, 200);

        assert_eq!("UpdateWeight", update_weight.description());
    }

    #[test]
    fn command_description_shutdown() {
        let shutdown: CommandType<&str, &str> = CommandType::Shutdown;

        assert_eq!("Shutdown", shutdown.description());
    }
}