jet1090 0.2.1

Real-time Mode S and ADS-B data with REST and ZMQ endpoints
use std::str::FromStr;

use rs1090::decode::{cpr::Position, TimedMessage};
use rs1090::prelude::*;
use serde::Serialize;
use tokio::sync::mpsc::Sender;

#[derive(Debug, Clone, Default, Serialize)]
pub struct Source {
    host: String,
    port: u16,
    rtlsdr: bool,
    pub airport: Option<String>,
    pub reference: Option<Position>,
    pub count: u64,
    pub last: u64,
}

impl FromStr for Source {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split('@').map(|p| p.trim()).collect();

        let mut source = Source::default();
        if parts.len() == 2 {
            if !parts[1].contains(',') {
                source.airport = Some(parts[1].to_string());
            }
            source.reference = Position::from_str(parts[1]).ok()
        };

        let parts: Vec<&str> = parts[0].split(':').map(|p| p.trim()).collect();
        if parts.len() == 1 {
            match parts[0] {
                "rtlsdr" => {
                    source.rtlsdr = true;
                }
                s if {
                    if let Ok(s) = s.parse::<u16>() {
                        source.host = "0.0.0.0".to_string();
                        source.port = s;
                        true
                    } else {
                        false
                    }
                } => {}
                _ => {
                    return Err("unparsable".to_string());
                }
            }
        } else {
            source.host = parts[0].to_string();
            if let Ok(port) = parts[1].parse::<u16>() {
                source.port = port;
            } else {
                return Err("unparsable port".to_string());
            }
        }
        Ok(source)
    }
}

impl Source {
    pub async fn receiver(&self, tx: Sender<TimedMessage>, idx: usize) {
        if self.rtlsdr {
            #[cfg(not(feature = "rtlsdr"))]
            {
                eprintln!(
                    "Not compiled with RTL-SDR support, use the rtlsdr feature"
                );
                std::process::exit(127);
            }
            #[cfg(feature = "rtlsdr")]
            {
                rtlsdr::discover();
                rtlsdr::receiver(tx, idx).await
            }
        } else {
            let server_address = format!("{}:{}", self.host, self.port);
            radarcape::receiver(server_address, tx, idx).await
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_source() {
        let source = Source::from_str("rtlsdr");
        assert!(source.is_ok());
        if let Ok(Source { rtlsdr, .. }) = source {
            assert!(rtlsdr);
        }

        let source = Source::from_str("rtlsdr@LFBO");
        assert!(source.is_ok());
        if let Ok(Source {
            rtlsdr,
            airport,
            reference: Some(pos),
            ..
        }) = source
        {
            assert!(rtlsdr);
            assert_eq!(airport, Some("LFBO".to_string()));
            assert_eq!(pos.latitude, 43.628101);
            assert_eq!(pos.longitude, 1.367263);
        }
        let source = Source::from_str("foo@LFBO");
        assert!(source.is_err());

        let source = Source::from_str("4003");
        assert!(source.is_ok());
        if let Ok(Source {
            host,
            port,
            rtlsdr,
            airport,
            reference,
            ..
        }) = source
        {
            assert!(!rtlsdr);
            assert_eq!(host, "0.0.0.0");
            assert_eq!(port, 4003);
            assert_eq!(airport, None);
            assert_eq!(reference, None);
        }

        let source = Source::from_str("4003@LFBO");
        assert!(source.is_ok());
        if let Ok(Source {
            host,
            port,
            rtlsdr,
            airport,
            reference: Some(pos),
            ..
        }) = source
        {
            assert!(!rtlsdr);
            assert_eq!(host, "0.0.0.0");
            assert_eq!(port, 4003);
            assert_eq!(airport, Some("LFBO".to_string()));
            assert_eq!(pos.latitude, 43.628101);
            assert_eq!(pos.longitude, 1.367263);
        }

        let source = Source::from_str("1.2.3.4:4003@LFBO");
        assert!(source.is_ok());
        if let Ok(Source {
            host,
            port,
            rtlsdr,
            airport,
            reference: Some(pos),
            ..
        }) = source
        {
            assert!(!rtlsdr);
            assert_eq!(host, "1.2.3.4");
            assert_eq!(port, 4003);
            assert_eq!(airport, Some("LFBO".to_string()));
            assert_eq!(pos.latitude, 43.628101);
            assert_eq!(pos.longitude, 1.367263);
        }
    }
}