eure_ls/native/
io_pool.rs1use std::fs;
4use std::thread::{self, JoinHandle};
5
6#[cfg(feature = "http")]
7use anyhow::anyhow;
8use crossbeam_channel::{Receiver, Sender, unbounded};
9#[cfg(feature = "http")]
10use eure::query::fetch_url;
11use eure::query::{TextFile, TextFileContent};
12
13pub struct IoRequest {
15 pub file: TextFile,
16}
17
18pub struct IoResponse {
20 pub file: TextFile,
21 pub result: Result<TextFileContent, anyhow::Error>,
23}
24
25pub struct IoPool {
30 request_tx: Sender<IoRequest>,
32 response_rx: Receiver<IoResponse>,
34 _workers: Vec<JoinHandle<()>>,
36}
37
38impl IoPool {
39 pub fn new(num_workers: usize) -> Self {
41 let (request_tx, request_rx) = unbounded::<IoRequest>();
42 let (response_tx, response_rx) = unbounded::<IoResponse>();
43
44 let mut workers = Vec::with_capacity(num_workers);
45
46 for i in 0..num_workers {
47 let request_rx = request_rx.clone();
48 let response_tx = response_tx.clone();
49
50 let handle = thread::Builder::new()
51 .name(format!("eure-ls-io-{}", i))
52 .spawn(move || {
53 worker_loop(request_rx, response_tx);
54 })
55 .expect("failed to spawn IO worker thread");
56
57 workers.push(handle);
58 }
59
60 Self {
61 request_tx,
62 response_rx,
63 _workers: workers,
64 }
65 }
66
67 pub fn request_file(&self, file: TextFile) {
71 let _ = self.request_tx.send(IoRequest { file });
73 }
74
75 pub fn receiver(&self) -> &Receiver<IoResponse> {
79 &self.response_rx
80 }
81}
82
83fn worker_loop(request_rx: Receiver<IoRequest>, response_tx: Sender<IoResponse>) {
85 for request in request_rx {
86 let result = read_file(&request.file);
87 let response = IoResponse {
88 file: request.file,
89 result,
90 };
91
92 if response_tx.send(response).is_err() {
94 break;
95 }
96 }
97}
98
99fn read_file(file: &TextFile) -> Result<TextFileContent, anyhow::Error> {
101 match file {
102 TextFile::Local(path) => Ok(TextFileContent(fs::read_to_string(path.as_ref())?)),
103 #[cfg(feature = "http")]
104 TextFile::Remote(url) => match fetch_url(url) {
105 Ok(content) => Ok(content),
106 Err(e) => Err(anyhow!("Failed to fetch {}: {}", url, e)),
107 },
108 #[cfg(not(feature = "http"))]
109 TextFile::Remote(url) => Err(anyhow::anyhow!(
110 "HTTP support not enabled, cannot fetch {}",
111 url
112 )),
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use std::path::PathBuf;
120
121 #[test]
122 fn test_read_nonexistent_file() {
123 let file = TextFile::from_path(PathBuf::from("/nonexistent/path/to/file.eure"));
124 let result = read_file(&file);
125 assert!(
126 result
127 .unwrap_err()
128 .downcast_ref::<std::io::Error>()
129 .is_some()
130 );
131 }
132}