things3_cloud/
cloud_writer.rs1use std::collections::BTreeMap;
2
3use anyhow::Result;
4use serde_json::json;
5use tracing::{debug, error};
6
7use crate::{
8 auth::load_auth,
9 client::ThingsCloudClient,
10 dirs::append_log_dir,
11 log_cache::read_cached_head_index,
12 wire::wire_object::WireObject,
13};
14
15pub trait CloudWriter {
16 fn commit(
17 &mut self,
18 changes: BTreeMap<String, WireObject>,
19 ancestor_index: Option<i64>,
20 ) -> Result<i64>;
21
22 fn head_index(&self) -> i64;
23}
24
25pub struct LoggingCloudWriter {
26 inner: Box<dyn CloudWriter>,
27}
28
29impl LoggingCloudWriter {
30 pub fn new(inner: Box<dyn CloudWriter>) -> Self {
31 Self { inner }
32 }
33}
34
35impl CloudWriter for LoggingCloudWriter {
36 fn commit(
37 &mut self,
38 changes: BTreeMap<String, WireObject>,
39 ancestor_index: Option<i64>,
40 ) -> Result<i64> {
41 let uuids = changes.keys().cloned().collect::<Vec<_>>();
42 let request_value = json!({
43 "ancestor_index": ancestor_index.unwrap_or(self.inner.head_index()),
44 "changes": &changes,
45 });
46 let request_json =
47 serde_json::to_string(&request_value).unwrap_or_else(|_| "{}".to_string());
48 debug!(
49 target: "things_cli::cloud_commit::request",
50 event = "cloud.commit.request",
51 ancestor_index,
52 change_count = uuids.len(),
53 uuids = ?uuids,
54 request_json = %request_json,
55 "cloud commit request"
56 );
57
58 match self.inner.commit(changes, ancestor_index) {
59 Ok(head_index) => {
60 debug!(
61 target: "things_cli::cloud_commit::success",
62 event = "cloud.commit.success",
63 ancestor_index,
64 change_count = uuids.len(),
65 uuids = ?uuids,
66 head_index,
67 "cloud commit succeeded"
68 );
69 Ok(head_index)
70 }
71 Err(err) => {
72 error!(
73 target: "things_cli::cloud_commit::error",
74 event = "cloud.commit.error",
75 ancestor_index,
76 change_count = uuids.len(),
77 uuids = ?uuids,
78 error = %err,
79 "cloud commit failed"
80 );
81 Err(err)
82 }
83 }
84 }
85
86 fn head_index(&self) -> i64 {
87 self.inner.head_index()
88 }
89}
90
91pub struct LiveCloudWriter {
92 client: ThingsCloudClient,
93}
94
95#[derive(Default)]
96pub struct DryRunCloudWriter {
97 head_index: i64,
98}
99
100impl DryRunCloudWriter {
101 pub fn new() -> Self {
102 Self::default()
103 }
104}
105
106impl LiveCloudWriter {
107 pub fn new() -> Result<Self> {
108 let (email, password) = load_auth()?;
109 let mut client = ThingsCloudClient::new(email, password)?;
110 let _ = client.authenticate();
111 client.head_index = read_cached_head_index(&append_log_dir());
112 Ok(Self { client })
113 }
114}
115
116impl CloudWriter for LiveCloudWriter {
117 fn commit(
118 &mut self,
119 changes: BTreeMap<String, WireObject>,
120 ancestor_index: Option<i64>,
121 ) -> Result<i64> {
122 self.client.commit(changes, ancestor_index)
123 }
124
125 fn head_index(&self) -> i64 {
126 self.client.head_index
127 }
128}
129
130impl CloudWriter for DryRunCloudWriter {
131 fn commit(
132 &mut self,
133 _changes: BTreeMap<String, WireObject>,
134 _ancestor_index: Option<i64>,
135 ) -> Result<i64> {
136 self.head_index += 1;
137 Ok(self.head_index)
138 }
139
140 fn head_index(&self) -> i64 {
141 self.head_index
142 }
143}