tech_ui/
resources.rs

1use std::{
2    io, fs,
3    sync::mpsc::{channel,Receiver,Sender,RecvTimeoutError,TryRecvError},
4    path::PathBuf,
5    time::{SystemTime,Duration},
6};
7
8
9pub struct Resource {
10    text: String,
11    updates: Option<Receiver<String>>,
12}
13impl Resource {
14    /*pub fn get_last(&self) -> &String {
15        &self.text
16    }*/
17    pub fn get(&mut self) -> &String {
18        if let Some(rx) = &self.updates {
19            let mut drop_rx = false;
20            loop {
21                match rx.try_recv() {
22                    Ok(t) => { self.text = t; },
23                    Err(TryRecvError::Empty) => break,
24                    Err(TryRecvError::Disconnected) => { drop_rx = true; break },
25                }
26            }
27            if drop_rx { self.updates = None; }
28        }
29        &self.text
30    }
31}
32
33pub struct ResourceManager {
34    sender: Option<Sender<ResourceInner>>,
35    handle: Option<std::thread::JoinHandle<()>>,
36}
37impl Drop for ResourceManager {
38    fn drop(&mut self) {
39        self.sender.take();
40        if let Some(h) = self.handle.take() {
41            h.join().ok();
42        }
43    }
44}
45impl ResourceManager {
46    pub fn new(int: Duration) -> ResourceManager {
47        let (tx,rx) = channel();
48        ResourceManager {
49            sender: Some(tx),
50            handle: Some(std::thread::spawn(move || {
51                let mut inners = Vec::new();
52                loop {
53                    match rx.recv_timeout(int) {
54                        Ok(inner) => inners.push(inner),
55                        Err(RecvTimeoutError::Disconnected) => break,
56                        Err(RecvTimeoutError::Timeout) => {}, 
57                    }
58                    while let Ok(inner) = rx.try_recv() {
59                        inners.push(inner);
60                    }
61                    for inner in &mut inners {
62                        if let Err(e) = inner.check_update() {
63                            log::warn!("Update failed for {:?}: {:?}",inner.path,e);
64                        }
65                    }
66                }
67            })),
68        }
69    }
70    pub fn register(&self, fl: &str, updates: bool) -> Result<Resource,io::Error> {
71        Ok(match updates {
72            false => resource_no_updates(fl)?,
73            true => {
74                let (mut rc,inner) = recource_with_updates(fl)?;
75                match &self.sender {
76                    None => { rc.updates = None; },
77                    Some(sender) => if let Err(_) = sender.send(inner) {
78                        rc.updates = None;
79                    },
80                }
81                rc
82            }
83        })
84    }
85    pub fn empty(&self) -> Resource {
86        Resource {
87            text: String::new(),
88            updates: None,
89        }
90    }
91}
92
93
94fn recource_with_updates(fl: &str) -> Result<(Resource,ResourceInner),io::Error> {
95    let path = PathBuf::from(fl);
96    let tm = fs::metadata(&path)?.modified()?;
97    let text = fs::read_to_string(&path)?;
98    let (tx,rx) = channel();
99    Ok((Resource { text, updates: Some(rx) }, ResourceInner { path, tm, sender: Some(tx) }))
100}
101
102fn resource_no_updates(fl: &str) -> Result<Resource,io::Error> {
103    let path = PathBuf::from(fl);
104    let text = fs::read_to_string(&path)?;
105    Ok(Resource { text, updates: None })
106}
107
108struct ResourceInner {
109    path: PathBuf,
110    tm: SystemTime,
111    sender: Option<Sender<String>>,
112}
113impl ResourceInner {    
114    fn check_update(&mut self) -> Result<bool,io::Error> {
115        let tm = fs::metadata(&self.path)?.modified()?;
116        let res =  tm > self.tm;
117        if res {            
118            self.tm = tm;
119            let text = fs::read_to_string(&self.path)?;
120            match &self.sender {
121                None => log::warn!("Resource is non-updatable: {:?}",self.path),
122                Some(sender) => match sender.send(text) {
123                    Err(_) => {
124                        log::warn!("Resource can't be updated: {:?}",self.path);
125                        self.sender = None;
126                    },
127                    Ok(()) => log::info!("Resource updated: {:?}",self.path),
128                },
129            }
130        }
131        Ok(res)
132    }
133}
134