use std::collections::BTreeMap; use anyhow::Result; use serde_json::json; use tracing::debug; use crate::{ client::ThingsCloudClient, common::{printable_json, printable_plain}, wire::wire_object::WireObject, }; pub trait CloudWriter { fn commit(&mut self, changes: BTreeMap) -> Result; fn head_index(&self) -> i64; } pub struct LoggingCloudWriter { inner: Box, } impl LoggingCloudWriter { pub fn new(inner: Box) -> Self { Self { inner } } } impl CloudWriter for LoggingCloudWriter { fn commit(&mut self, changes: BTreeMap) -> Result { // a commit builds on the client's head // that is the synced head or the one the previous commit returned let ancestor_index = self.inner.head_index(); let uuids = changes.keys().cloned().collect::>(); // the payload is serialized for the log only when that log is on let request_json = if tracing::enabled!(target: "things::cloud::commit::request", tracing::Level::DEBUG) { serde_json::to_string(&json!({ "ancestor_index": ancestor_index, "changes": &changes, })) .unwrap_or_else(|error| json!({ "error": error.to_string() }).to_string()) } else { String::new() }; debug!( target: "things::cloud::commit::request", event = "cloud.commit.request", ancestor_index, change_count = uuids.len(), uuids = ?uuids, request_json = %printable_json(&request_json), "cloud commit request" ); match self.inner.commit(changes) { Ok(head_index) => { debug!( target: "things::cloud::commit::success", event = "cloud.commit.success", ancestor_index, change_count = uuids.len(), uuids = ?uuids, head_index, "cloud commit succeeded" ); Ok(head_index) } // the command returns the error // the event is for a debug log Err(err) => { debug!( target: "things::cloud::commit::error", event = "cloud.commit.error", ancestor_index, change_count = uuids.len(), uuids = ?uuids, error = %printable_plain(&format!("{err:#}")), "cloud commit failed" ); Err(err) } } } fn head_index(&self) -> i64 { self.inner.head_index() } } /// commits against the history and head of the client that synchronized this run's state pub struct LiveCloudWriter { client: ThingsCloudClient, } #[derive(Default)] pub struct DryRunCloudWriter { head_index: i64, } impl DryRunCloudWriter { pub fn new() -> Self { Self::default() } } impl LiveCloudWriter { pub fn new(client: ThingsCloudClient) -> Self { Self { client } } } impl CloudWriter for LiveCloudWriter { fn commit(&mut self, changes: BTreeMap) -> Result { self.client.commit(changes) } fn head_index(&self) -> i64 { self.client.head_index } } impl CloudWriter for DryRunCloudWriter { fn commit(&mut self, _changes: BTreeMap) -> Result { self.head_index += 1; Ok(self.head_index) } fn head_index(&self) -> i64 { self.head_index } }