Skip to main content

rig_core/conversation/
aside.rs

1//! Records kept in a conversation that never reach a model.
2
3use std::fmt;
4use std::str::FromStr;
5
6use serde::{Deserialize, Serialize};
7
8use super::{Author, MessageId, ParseIdError, parse_id};
9
10/// Kept with the conversation, saved and forked with it, never sent to a
11/// model: a plugin's note in the transcript, the kernel's record of a call
12/// in flight, a plugin's own data. Readers take the kinds they know.
13#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
14pub struct Aside {
15    /// What it is, chosen by its author: `"note"` (a transcript
16    /// [`Note`](super::Note)), `"rig.call"` (the kernel's call-in-flight
17    /// record), a plugin's own kind.
18    pub kind: String,
19    /// The entry it follows; `None` before the first. While an edit took
20    /// that entry out it follows [`start_of`](super::Conversation::start_of)
21    /// it.
22    #[serde(default, skip_serializing_if = "Option::is_none")]
23    pub after: Option<MessageId>,
24    /// Who put it in.
25    pub author: Author,
26    /// Its data, verbatim.
27    #[serde(default, skip_serializing_if = "serde_json::Value::is_null")]
28    pub value: serde_json::Value,
29    /// Whether it is saved with the conversation (the default). An unsaved
30    /// one lives as long as the value: no change records it.
31    #[serde(default = "yes", skip_serializing_if = "is_yes")]
32    pub saved: bool,
33}
34
35fn yes() -> bool {
36    true
37}
38
39fn is_yes(saved: &bool) -> bool {
40    *saved
41}
42
43impl Aside {
44    /// A saved aside of `kind` by `author`, holding `value`, before the
45    /// first entry.
46    pub fn new(kind: impl Into<String>, author: Author, value: serde_json::Value) -> Self {
47        Self {
48            kind: kind.into(),
49            after: None,
50            author,
51            value,
52            saved: true,
53        }
54    }
55
56    /// This aside, following entry `after` (`None`: before the first).
57    #[must_use]
58    pub fn after(self, after: Option<MessageId>) -> Self {
59        Self { after, ..self }
60    }
61
62    /// This aside, never saved.
63    #[must_use]
64    pub fn unsaved(self) -> Self {
65        Self {
66            saved: false,
67            ..self
68        }
69    }
70}
71
72/// An aside's identity in its conversation, kept while it is replaced and
73/// across a save. Rises in the order asides were put.
74#[derive(
75    Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
76)]
77#[serde(transparent)]
78pub struct AsideId(pub(super) u64);
79
80impl AsideId {
81    /// Its number: ids rise from 0 in the order asides were put.
82    pub const fn get(self) -> u64 {
83        self.0
84    }
85}
86
87/// Its plain form, `a` and its number (`a4`): how commands name an aside.
88impl fmt::Display for AsideId {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "a{}", self.0)
91    }
92}
93
94/// Reads its plain form, `a4`, or the number alone.
95impl FromStr for AsideId {
96    type Err = ParseIdError;
97
98    fn from_str(text: &str) -> Result<Self, ParseIdError> {
99        parse_id(text, 'a').map(Self)
100    }
101}