1use std::path::Path;
2
3use made_proto::v1::{GenerateCeremonyReportRequest, GenerateCeremonyReportResponse};
4use tokio::io::AsyncWriteExt;
5use uuid::Uuid;
6
7use crate::{MadeClient, MadeClientError};
8
9impl MadeClient {
10 pub async fn generate_report(
11 &self,
12 ceremony_ids: Vec<String>,
13 title: impl Into<String>,
14 ) -> Result<GenerateCeremonyReportResponse, MadeClientError> {
15 self.rpc()
16 .generate_ceremony_report(Self::request(
17 &self.context(),
18 "/underpass.made.v1.MadeService/GenerateCeremonyReport",
19 GenerateCeremonyReportRequest {
20 ceremony_ids,
21 title: title.into(),
22 },
23 ))
24 .await
25 .map(tonic::Response::into_inner)
26 .map_err(MadeClientError::from_status)
27 }
28
29 pub async fn export_report(
30 &self,
31 ceremony_ids: Vec<String>,
32 title: impl Into<String>,
33 destination: &Path,
34 ) -> Result<GenerateCeremonyReportResponse, MadeClientError> {
35 let report = self.generate_report(ceremony_ids, title).await?;
36 let parent = destination.parent().unwrap_or_else(|| Path::new("."));
37 tokio::fs::create_dir_all(parent)
38 .await
39 .map_err(|error| MadeClientError::io(parent, error))?;
40 let name = destination
41 .file_name()
42 .and_then(|name| name.to_str())
43 .unwrap_or("report.md");
44 let temporary = parent.join(format!(".{name}.{}.part", Uuid::new_v4()));
45 let result = async {
46 let mut file = tokio::fs::File::create(&temporary)
47 .await
48 .map_err(|error| MadeClientError::io(&temporary, error))?;
49 file.write_all(report.report_markdown.as_bytes())
50 .await
51 .map_err(|error| MadeClientError::io(&temporary, error))?;
52 file.sync_all()
53 .await
54 .map_err(|error| MadeClientError::io(&temporary, error))?;
55 drop(file);
56 tokio::fs::rename(&temporary, destination)
57 .await
58 .map_err(|error| MadeClientError::io(destination, error))
59 }
60 .await;
61 if result.is_err() {
62 let _ = tokio::fs::remove_file(&temporary).await;
63 }
64 result.map(|()| report)
65 }
66}