Skip to main content

things3_cloud/
cloud_writer.rs

1use 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}