You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.1 KiB
102 lines
3.1 KiB
//! Mutating post body
|
|
use super::*;
|
|
|
|
/// A message that can be mutated by deltas.
|
|
pub type MessageSpan = Vec<char>;
|
|
|
|
/// Contains post deltas and an intermediate representation of the still-open post body.
|
|
/// Created and modified with `Kokoro` worker instances.
|
|
///
|
|
/// This should not be created by itself, instead `Kokoro` should create instances of this, so that it can retain the `watch::Sender` and other such things.
|
|
#[derive(Debug)]
|
|
pub struct Karada
|
|
{
|
|
/// The post body so far as a vector of `char`s.
|
|
pub(super) scape: Arc<RwLock<MessageSpan>>,
|
|
/// All applied deltas so far. Last applied one is at the end.
|
|
pub(super) deltas: Arc<RwLock<Vec<Delta>>>,
|
|
|
|
/// the latest render of the whole body string. Updated whenever a delta(s) are applied atomically.
|
|
pub(super) current_body: watch::Receiver<String>,
|
|
}
|
|
|
|
impl Karada
|
|
{
|
|
/// Clone the body string
|
|
pub fn body(&self) -> String
|
|
{
|
|
self.current_body.borrow().to_owned()
|
|
}
|
|
|
|
/// Create the deltas required to atomically transform current body into `new`.
|
|
///
|
|
/// Return the number of deltas added to `output`.
|
|
pub fn create_body_deltas<T: BackInserter<Delta> + ?Sized, U: AsRef<str>>(&self, output: &mut T, new: U) -> usize
|
|
{
|
|
let body = self.body();
|
|
let new = new.as_ref();
|
|
delta::infer_deltas(output, &body[..], new)
|
|
}
|
|
|
|
/// Consume this instance into a suspension
|
|
///
|
|
/// This will only acquire locks if needed, but since they might be needed, it must be awaited in case of `Kokoro` instances potentially owning the data.
|
|
pub async fn into_suspended(self) -> Suspension
|
|
{
|
|
let scape: String = {
|
|
let scape = self.scape;
|
|
match Arc::try_unwrap(scape) { //try to unwrap if possible, to avoid acquiring useless lock
|
|
Ok(scape) => scape.into_inner().into_iter().collect(),
|
|
Err(scape) => scape.read().await.iter().collect(),
|
|
}
|
|
};
|
|
let deltas: Vec<Delta> = {
|
|
let deltas = self.deltas;
|
|
match Arc::try_unwrap(deltas) {
|
|
Ok(deltas) => deltas.into_inner(),
|
|
Err(deltas) => deltas.read().await.clone(),
|
|
}
|
|
};
|
|
|
|
Suspension{scape,deltas}
|
|
}
|
|
|
|
pub(super) fn from_suspended(susp: Suspension, current_body: watch::Receiver<String>) -> Self
|
|
{
|
|
Self {
|
|
scape: Arc::new(RwLock::new(susp.scape.chars().collect())),
|
|
deltas: Arc::new(RwLock::new(susp.deltas)),
|
|
current_body,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Suspension of [`Karada`](Karada).
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
pub struct Suspension
|
|
{
|
|
pub(super) scape: String,
|
|
pub(super) deltas: Vec<Delta>,
|
|
}
|
|
|
|
use suspend::{
|
|
Suspendable,
|
|
SuspendStream,
|
|
};
|
|
|
|
#[async_trait]
|
|
impl Suspendable for Suspension
|
|
{
|
|
async fn suspend<S: SuspendStream +Send+Sync + ?Sized>(self, into: &mut S) -> Result<(), suspend::Error>
|
|
{
|
|
let mut output = suspend::Object::new();
|
|
output.insert("post-dynamic", self);
|
|
into.set_object(output).await
|
|
}
|
|
async fn load<S: SuspendStream +Send+Sync+ ?Sized>(from: &mut S) -> Result<Self, suspend::Error>
|
|
{
|
|
let mut input = from.get_object().await?.ok_or(suspend::Error::BadObject)?;
|
|
input.try_get("post-dynamic").ok_or(suspend::Error::BadObject)
|
|
}
|
|
}
|