1use crate::*;
2use anyhow::Result;
3use std::{
4 fmt::Debug,
5 fs,
6 future::Future,
7 io::{self, Read, Write},
8 path::{Path, PathBuf},
9 pin::Pin,
10 time::SystemTime,
11};
12use tracing_subscriber::{EnvFilter, fmt};
13
14
15pub fn initialize() {
17 let env_log = match EnvFilter::try_from_env("LOG") {
18 Ok(env_value_from_env) => env_value_from_env,
19 Err(_) => EnvFilter::from("info"),
20 };
21 fmt()
22 .compact()
23 .with_thread_names(false)
24 .with_thread_ids(false)
25 .with_ansi(true)
26 .with_env_filter(env_log)
27 .with_filter_reloading()
28 .init();
29}
30
31
32pub fn put_to_clipboard(text: &str) -> Result<()> {
33 let mut clipboard = clippers::Clipboard::get();
34 clipboard.write_text(text)?;
35 Ok(())
36}
37
38
39pub fn local_file_size<P: AsRef<Path>>(file_path: P) -> Result<u64> {
40 let metadata = fs::metadata(file_path)?;
41 Ok(metadata.len())
42}
43
44
45pub async fn wait_for_file<P>(path: P) -> Result<()>
46where
47 P: AsRef<Path> + Debug,
48{
49 fn check(
50 path: PathBuf,
51 last_size: u64,
52 last_mtime: Option<SystemTime>,
53 stable_intervals: u32,
54 attempts: u32,
55 ) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
56 Box::pin(async move {
57 if attempts >= 20 {
58 warn!("File {path:?} did not stabilize after 5 seconds");
59 Ok(())
60 } else {
61 let (new_size, new_mtime) = fs::metadata(&path)
62 .map(|m| (m.len(), m.modified().ok()))
63 .unwrap_or((0, None));
64
65 let current_stable_intervals =
66 if new_size > 0 && new_size == last_size && new_mtime == last_mtime {
67 stable_intervals + 1
68 } else {
69 0
70 };
71
72 if current_stable_intervals >= 2 {
73 debug!("File {:?} is stable after {}ms", path, attempts * 250);
74 Ok(())
75 } else {
76 tokio::time::sleep(std::time::Duration::from_millis(250)).await;
77 check(
78 path,
79 new_size,
80 new_mtime,
81 current_stable_intervals,
82 attempts + 1,
83 )
84 .await
85 }
86 }
87 })
88 }
89
90 check(path.as_ref().to_path_buf(), 0, None, 0, 0).await
91}
92
93
94pub fn size_kib(size_in_bytes: u64) -> f64 {
95 (size_in_bytes as f64) / 1024.0
96}
97
98
99pub fn file_extension<P: AsRef<Path>>(path: P) -> String {
100 path.as_ref()
101 .extension()
102 .and_then(|e| e.to_str())
103 .map(|e| format!(".{e}"))
104 .unwrap_or_default()
105}
106
107
108pub fn stream_file_to_remote<R, W>(
109 reader: &mut R,
110 writer: &mut W,
111 buffer_size: usize,
112 total_size: u64,
113) -> Result<()>
114where
115 R: Read,
116 W: Write,
117{
118 let mut buffer = vec![0u8; buffer_size];
119 let chunks = if total_size > 0 {
121 (total_size / buffer_size as u64) + 1
122 } else {
123 1
124 };
125
126 info!(
127 "Streaming file of size: {:.2}KiB to remote server..",
128 size_kib(total_size)
129 );
130
131 let mut chunk_index = 0u64;
132 loop {
133 let bytes_read = reader.read(&mut buffer)?;
134 if bytes_read == 0 {
135 break;
136 }
137
138 writer.write_all(&buffer[..bytes_read])?;
139 chunk_index += 1;
140
141 let percent = if chunks > 0 {
142 (chunk_index as f64 * 100.0) / chunks as f64
143 } else {
144 100.0
145 };
146
147 eprint!("\rProgress: {percent:.2}% ");
148 io::stderr().flush()?;
149 }
150
151 eprintln!(); info!("Upload complete!");
153 Ok(())
154}