telemetric_client/
client.rs1use std::{
2 io::Write as _,
3 sync::atomic::{AtomicBool, Ordering},
4};
5
6use anyhow::anyhow;
7use futures::StreamExt;
8use telemetric_messages as messages;
9use yaaral::http::{self, HttpClientInterface, Response};
10
11pub struct Client<RT: HttpClientInterface> {
13 runtime: RT,
14 uri: String,
15 software_id: String,
16 reporting_enabled: AtomicBool,
17 app_info: (String, String, String),
18}
19
20impl<RT: HttpClientInterface> Client<RT> {
21 pub fn new(
24 runtime: RT,
25 uri: impl Into<String>,
26 software_id: impl Into<String>,
27 app_info: (impl Into<String>, impl Into<String>, impl Into<String>),
28 ) -> Self {
29 Self {
30 runtime,
31 uri: uri.into(),
32 software_id: software_id.into(),
33 reporting_enabled: true.into(),
34 app_info: (app_info.0.into(), app_info.1.into(), app_info.2.into()),
35 }
36 }
37 pub fn enable_reporting(&self) {
39 self.reporting_enabled.store(true, Ordering::Relaxed);
40 }
41 pub fn disable_reporting(&self) {
43 self.reporting_enabled.store(false, Ordering::Relaxed);
44 }
45 pub async fn report(&self, key: String, value: impl serde::Serialize) -> anyhow::Result<()> {
47 if !self.reporting_enabled.load(Ordering::Relaxed) {
48 return Ok(());
49 }
50 let measurement = messages::Measurement {
51 key,
52 software_id: self.software_id.clone(),
53 value: serde_json::to_value(value)?,
54 };
55 let request =
56 http::Request::from_uri(format!("{}/api/report", self.uri)).json(&measurement)?;
57 let respnse = self.runtime.wpost(request).await;
58 if respnse.status().is_success() {
59 let mut stream = respnse.into_stream();
60 let mut buf = Vec::new();
61 while let Some(chunk) = stream.next().await {
62 let chunk = chunk.map_err(|e| anyhow!("{:?}", e))?;
63 buf.extend_from_slice(&chunk);
64 }
65 let answer: messages::Answer = serde_json::from_slice(&buf)?;
66 if answer.success {
67 let app_data_dir = directories::ProjectDirs::from(
69 &self.app_info.0,
70 &self.app_info.1,
71 &self.app_info.2,
72 )
73 .ok_or(anyhow!("Cannot locate app directory."))?;
74 let data_dir = app_data_dir.data_dir();
75 std::fs::create_dir_all(data_dir)?;
76
77 let uuid_file = data_dir.join("uuids.txt");
78 let mut uuid_file = std::fs::OpenOptions::new()
79 .create(true)
80 .append(true)
81 .open(uuid_file)?;
82 writeln!(uuid_file, "{}", answer.uuid)?;
83
84 let data_file = data_dir.join("data.yaml");
86 let mut data_file = std::fs::OpenOptions::new()
87 .create(true)
88 .append(true)
89 .open(data_file)?;
90
91 let yaml_data = serde_json::to_string(&measurement)?;
92 writeln!(data_file, "- {}", yaml_data)?;
93 } else {
94 return Err(anyhow!("Server error: {:?}", answer.message));
95 }
96 Ok(())
97 } else {
98 Err(anyhow!("Request error: {}", respnse.status()))
99 }
100 }
101}