1use std::path::Path;
3
4use anyhow::{Context, Result};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct FileStats {
8 pub lines: usize,
9 pub words: usize,
10 pub bytes: u64,
11}
12
13pub async fn read_file(path: impl AsRef<Path>) -> Result<String> {
15 crate::platform::read_file(path).await
16}
17
18fn count_lines_and_words(path: &Path) -> Result<(usize, usize)> {
19 use std::io::{BufRead, BufReader};
20 let file = std::fs::File::open(path)
21 .with_context(|| format!("Failed to open file '{}'", path.display()))?;
22 let reader = BufReader::new(file);
23 let mut lines = 0usize;
24 let mut words = 0usize;
25 for line in reader.lines() {
26 let line =
27 line.with_context(|| format!("Failed to read line from '{}'", path.display()))?;
28 lines += 1;
29 words += line.split_whitespace().count();
30 }
31 Ok((lines, words))
32}
33
34pub async fn file_stats(path: impl AsRef<Path>) -> Result<FileStats> {
35 let path = path.as_ref();
36
37 #[cfg(not(target_arch = "wasm32"))]
38 let bytes = tokio::fs::metadata(path)
39 .await
40 .with_context(|| format!("Failed to read metadata for '{}'", path.display()))?
41 .len();
42
43 #[cfg(target_arch = "wasm32")]
44 let bytes = std::fs::metadata(path)
45 .with_context(|| format!("Failed to read metadata for '{}'", path.display()))?
46 .len();
47
48 let path_owned = path.to_path_buf();
49
50 #[cfg(not(target_arch = "wasm32"))]
51 let (lines, words) = tokio::task::spawn_blocking(move || count_lines_and_words(&path_owned))
52 .await
53 .context("Blocking task panicked")??;
54
55 #[cfg(target_arch = "wasm32")]
56 let (lines, words) = count_lines_and_words(&path_owned)?;
57
58 Ok(FileStats {
59 lines,
60 words,
61 bytes,
62 })
63}