Skip to main content

things3_cloud/
cloud_writer.rs

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