reedline/history/
item.rs

1use chrono::Utc;
2#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))]
3use rusqlite::ToSql;
4use serde::{de::DeserializeOwned, Deserialize, Serialize};
5use std::{fmt::Display, time::Duration};
6
7/// Unique ID for the [`HistoryItem`]. More recent items have higher ids than older ones.
8#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9pub struct HistoryItemId(pub i64);
10impl HistoryItemId {
11    /// Create a new `HistoryItemId` value
12    pub const fn new(i: i64) -> HistoryItemId {
13        HistoryItemId(i)
14    }
15}
16
17impl Display for HistoryItemId {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        write!(f, "{}", self.0)
20    }
21}
22
23/// Unique ID for the session in which reedline was run to disambiguate different sessions
24#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
25pub struct HistorySessionId(pub(crate) i64);
26impl HistorySessionId {
27    pub(crate) const fn new(i: i64) -> HistorySessionId {
28        HistorySessionId(i)
29    }
30}
31
32impl Display for HistorySessionId {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.0)
35    }
36}
37
38#[cfg(any(feature = "sqlite", feature = "sqlite-dynlib"))]
39impl ToSql for HistorySessionId {
40    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
41        Ok(rusqlite::types::ToSqlOutput::Owned(
42            rusqlite::types::Value::Integer(self.0),
43        ))
44    }
45}
46
47impl From<HistorySessionId> for i64 {
48    fn from(id: HistorySessionId) -> Self {
49        id.0
50    }
51}
52
53/// This trait represents additional arbitrary context to be added to a history (optional, see [`HistoryItem`])
54pub trait HistoryItemExtraInfo: Serialize + DeserializeOwned + Default + Send {}
55
56#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
57/// something that is serialized as null and deserialized by ignoring everything
58pub struct IgnoreAllExtraInfo;
59
60impl Serialize for IgnoreAllExtraInfo {
61    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
62    where
63        S: serde::Serializer,
64    {
65        Option::<IgnoreAllExtraInfo>::None.serialize(serializer)
66    }
67}
68
69impl<'de> Deserialize<'de> for IgnoreAllExtraInfo {
70    fn deserialize<D>(d: D) -> std::result::Result<Self, D::Error>
71    where
72        D: serde::Deserializer<'de>,
73    {
74        serde::de::IgnoredAny::deserialize(d).map(|_| IgnoreAllExtraInfo)
75    }
76}
77
78impl HistoryItemExtraInfo for IgnoreAllExtraInfo {}
79
80/// Represents one run command with some optional additional context
81#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
82pub struct HistoryItem<ExtraInfo: HistoryItemExtraInfo = IgnoreAllExtraInfo> {
83    /// primary key, unique across one history
84    pub id: Option<HistoryItemId>,
85    /// date-time when this command was started
86    pub start_timestamp: Option<chrono::DateTime<Utc>>,
87    /// the full command line as text
88    pub command_line: String,
89    /// a unique id for one shell session.
90    /// used so the history can be filtered to a single session
91    pub session_id: Option<HistorySessionId>,
92    /// the hostname the commands were run in
93    pub hostname: Option<String>,
94    /// the current working directory
95    pub cwd: Option<String>,
96    /// the duration the command took to complete
97    pub duration: Option<Duration>,
98    /// the exit status of the command
99    pub exit_status: Option<i64>,
100    /// arbitrary additional information that might be interesting
101    /// NOTE: this attribute is required because of
102    /// <https://github.com/rust-lang/rust/issues/41617>
103    ///       (see <https://github.com/serde-rs/serde/issues/1296#issuecomment-394056188> for the fix)
104    #[serde(deserialize_with = "Option::<ExtraInfo>::deserialize")]
105    pub more_info: Option<ExtraInfo>,
106}
107
108impl HistoryItem {
109    /// create a history item purely from the command line with everything else set to None
110    pub fn from_command_line(cmd: impl Into<String>) -> HistoryItem {
111        HistoryItem {
112            id: None,
113            start_timestamp: None,
114            command_line: cmd.into(),
115            session_id: None,
116            hostname: None,
117            cwd: None,
118            duration: None,
119            exit_status: None,
120            more_info: None,
121        }
122    }
123}