ledcat 0.2.0

Control lots of LED's over lots of protocols
use std::borrow::Cow;
use std::fs;
use std::io::{self, BufRead};
use std::net;
use std::path;
use std::sync::{Arc, RwLock};
use std::thread;
use std::time;

pub trait Target: Send {
    fn addresses(&self) -> Cow<[net::SocketAddr]>;
}

impl Target for Vec<net::SocketAddr> {
    fn addresses(&self) -> Cow<[net::SocketAddr]> {
        Cow::Borrowed(self)
    }
}

pub struct Broadcast {}

impl Target for Broadcast {
    fn addresses(&self) -> Cow<[net::SocketAddr]> {
        let ip = net::Ipv4Addr::new(255, 255, 255, 255);
        let addrs = vec![net::SocketAddrV4::new(ip, super::PORT).into()];
        Cow::Owned(addrs)
    }
}

pub struct ListFile {
    cache: Arc<RwLock<Vec<net::SocketAddr>>>,
}

impl ListFile {
    pub fn new<T: Into<path::PathBuf>>(p: T) -> ListFile {
        let path = p.into();
        let cache = Arc::new(RwLock::new(Vec::new()));

        let cache_weak = Arc::downgrade(&cache);
        thread::spawn(move || {
            macro_rules! try_or_continue {
                ($expr:expr) => {{
                    match $expr {
                        Ok(t) => t,
                        Err(_) => continue,
                    }
                }};
            }

            let mut prev_mod_time = None;
            loop {
                let meta = try_or_continue!(fs::metadata(&path));
                let mod_time = try_or_continue!(meta.modified());
                let reload = prev_mod_time != Some(mod_time);
                prev_mod_time = Some(mod_time);

                if reload {
                    let cache = match cache_weak.upgrade() {
                        Some(c) => c,
                        None => return,
                    };
                    let mut v = cache.write().unwrap();
                    v.clear();

                    let file = try_or_continue!(fs::File::open(&path));
                    let addrs = io::BufReader::new(file)
                        .lines()
                        .filter_map(|rs| rs.ok())
                        .filter_map(|line| {
                            line.parse().ok().or_else(|| {
                                line.parse()
                                    .ok()
                                    .map(|ip| net::SocketAddr::new(ip, super::PORT))
                            })
                        });
                    v.extend(addrs);
                    v.dedup();
                }

                thread::sleep(time::Duration::from_secs(1));
            }
        });

        ListFile { cache }
    }
}

impl Target for ListFile {
    fn addresses(&self) -> Cow<[net::SocketAddr]> {
        Cow::Owned(self.cache.read().unwrap().clone())
    }
}