use serde::{Deserialize, Serialize};
use super::{Aside, AsideId, Author, Conversation, MessageId};
pub const TURN: &str = "rig.turn";
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct TurnRecord {
pub index: u64,
pub start: MessageId,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end: Option<TurnEnd>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "how", rename_all = "snake_case")]
pub enum TurnEnd {
Answered {
#[serde(default, rename = "by", skip_serializing_if = "Option::is_none")]
answer: Option<MessageId>,
},
Failed {
why: String,
},
Stopped {
#[serde(default, skip_serializing_if = "Option::is_none")]
why: Option<String>,
},
}
impl TurnRecord {
pub fn of(aside: &Aside) -> Option<Self> {
(aside.kind == TURN)
.then(|| Self::deserialize(&aside.value).ok())
.flatten()
}
}
impl Conversation {
pub fn turns(&self) -> impl DoubleEndedIterator<Item = (AsideId, TurnRecord)> + '_ {
self.asides()
.filter_map(|(id, aside)| Some((id, TurnRecord::of(aside)?)))
}
pub fn open_turn(&mut self) -> TurnRecord {
let last = self.turns().next_back().map(|(_, record)| record.index);
let record = TurnRecord {
index: last.map_or(0, |index| index.saturating_add(1)),
start: self.turn_start(),
end: None,
};
let after = self.entries().next_back().map(|entry| entry.id);
let value = serde_json::to_value(&record).unwrap_or_default();
self.add_aside(Aside::new(TURN, Author::rig(), value).after(after));
record
}
pub fn end_turn(&mut self, end: TurnEnd) -> bool {
let Some((id, record)) = self.turns().next_back() else {
return false;
};
let Some(aside) = self.aside(id).filter(|_| record.end.is_none()).cloned() else {
return false;
};
let ended = TurnRecord {
end: Some(end),
..record
};
let value = serde_json::to_value(ended).unwrap_or_default();
self.replace_aside(id, Aside { value, ..aside })
}
}