rlibdns 0.1.211

Rust DNS Library
Documentation
use std::any::Any;
use std::collections::HashMap;
use std::fmt;
use std::fmt::Formatter;
use crate::messages::inter::rr_types::RRTypes;
use crate::rr_data::inter::rr_data::{RRData, RRDataError};
use crate::utils::coord_utils::{encode_loc_precision, CoordUtils};
use crate::zone::inter::zone_rr_data::ZoneRRData;
use crate::zone::zone_reader::{ErrorKind, ZoneReaderError};

#[derive(Clone, Debug)]
pub struct LocRRData {
    version: u8,
    size: u8,
    h_precision: u8,
    v_precision: u8,
    latitude: u32,
    longitude: u32,
    altitude: u32
}

impl Default for LocRRData {

    fn default() -> Self {
        Self {
            version: 0,
            size: 0,
            h_precision: 0,
            v_precision: 0,
            latitude: 0,
            longitude: 0,
            altitude: 0
        }
    }
}

impl RRData for LocRRData {

    fn from_bytes(buf: &[u8], off: usize) -> Result<Self, RRDataError> {
        let length = u16::from_be_bytes([buf[off], buf[off+1]]);
        if length == 0 {
            return Ok(Default::default());
        }

        let version = buf[off+2];
        let size = buf[off+3];
        let h_precision = buf[off+4];
        let v_precision = buf[off+5];
        let latitude = u32::from_be_bytes([buf[off+6], buf[off+7], buf[off+8], buf[off+9]]);
        let longitude = u32::from_be_bytes([buf[off+10], buf[off+11], buf[off+12], buf[off+13]]);
        let altitude = u32::from_be_bytes([buf[off+14], buf[off+15], buf[off+16], buf[off+17]]);

        Ok(Self {
            version,
            size,
            h_precision,
            v_precision,
            latitude,
            longitude,
            altitude
        })
    }

    fn to_bytes_compressed(&self, _compression_data: &mut HashMap<String, usize>, _off: usize) -> Result<Vec<u8>, RRDataError> {
        self.to_bytes()
    }

    fn to_bytes(&self) -> Result<Vec<u8>, RRDataError> {
        let mut buf = vec![0u8; 18];

        buf[3] = self.version;
        buf[4] = self.size;
        buf[5] = self.h_precision;
        buf[6] = self.v_precision;
        buf.splice(6..10, self.latitude.to_be_bytes());
        buf.splice(10..14, self.longitude.to_be_bytes());
        buf.splice(14..18, self.altitude.to_be_bytes());

        buf.splice(0..2, ((buf.len()-2) as u16).to_be_bytes());

        Ok(buf)
    }

    fn get_type(&self) -> RRTypes {
        RRTypes::Loc
    }

    fn upcast(self) -> Box<dyn RRData> {
        Box::new(self)
    }

    fn as_any(&self) -> &dyn Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }

    fn clone_box(&self) -> Box<dyn RRData> {
        Box::new(self.clone())
    }
}

impl LocRRData {

    pub fn new(version: u8, size: u8, h_precision: u8, v_precision: u8, latitude: u32, longitude: u32, altitude: u32) -> Self {
        Self {
            version,
            size,
            h_precision,
            v_precision,
            latitude,
            longitude,
            altitude
        }
    }

    pub fn set_version(&mut self, version: u8) {
        self.version = version;
    }

    pub fn get_version(&self) -> u8 {
        self.version
    }

    pub fn set_size(&mut self, size: u8) {
        self.size = size;
    }

    pub fn get_size(&self) -> u8 {
        self.size
    }

    pub fn set_h_precision(&mut self, h_precision: u8) {
        self.h_precision = h_precision;
    }

    pub fn get_h_precision(&self) -> u8 {
        self.h_precision
    }

    pub fn set_v_precision(&mut self, h_precision: u8) {
        self.v_precision = h_precision;
    }

    pub fn get_v_precision(&self) -> u8 {
        self.v_precision
    }
    
    pub fn set_latitude(&mut self, latitude: u32) {
        self.latitude = latitude;
    }
    
    pub fn get_latitude(&self) -> u32 {
        self.latitude
    }
    
    pub fn set_longitude(&mut self, longitude: u32) {
        self.longitude = longitude;
    }
    
    pub fn get_longitude(&self) -> u32 {
        self.longitude
    }
    
    pub fn set_altitude(&mut self, altitude: u32) {
        self.altitude = altitude;
    }
    
    pub fn get_altitude(&self) -> u32 {
        self.altitude
    }
}

impl ZoneRRData for LocRRData {

    fn set_data(&mut self, index: usize, value: &str) -> Result<(), ZoneReaderError> {
        Ok(match index {
            0 => self.latitude = value.parse::<u32>()
                .map_err(|_| ZoneReaderError::new(ErrorKind::FormErr, &format!("unable to parse latitude 1 param for record type {}", self.get_type())))? * 3_600_000,
            1 => self.latitude = self.latitude + value.parse::<u32>()
                .map_err(|_| ZoneReaderError::new(ErrorKind::FormErr, &format!("unable to parse latitude 2 param for record type {}", self.get_type())))? * 60_000,
            2 => self.latitude += (value.parse::<f64>()
                .map_err(|_| ZoneReaderError::new(ErrorKind::FormErr, &format!("unable to parse latitude 3 param for record type {}", self.get_type())))? * 1000.0).round() as u32,
            3 => {
                let sign = match value {
                    "S" | "W" => -1,
                    "N" | "E" => 1,
                    _ => return Err(ZoneReaderError::new(ErrorKind::FormErr, &format!("invalid direction for record type {}", self.get_type())))
                };

                let val = (sign * (self.latitude as i64)) + (1 << 31);
                self.latitude = val as u32
            }
            4 => self.longitude = value.parse::<u32>()
                .map_err(|_| ZoneReaderError::new(ErrorKind::FormErr, &format!("unable to parse longitude 1 param for record type {}", self.get_type())))? * 3_600_000,
            5 => self.longitude = self.longitude + value.parse::<u32>()
                .map_err(|_| ZoneReaderError::new(ErrorKind::FormErr, &format!("unable to parse longitude 2 param for record type {}", self.get_type())))? * 60_000,
            6 => self.longitude += (value.parse::<f64>()
                .map_err(|_| ZoneReaderError::new(ErrorKind::FormErr, &format!("unable to parse longitude 3 param for record type {}", self.get_type())))? * 1000.0).round() as u32,
            7 => {
                let sign = match value {
                    "S" | "W" => -1,
                    "N" | "E" => 1,
                    _ => return Err(ZoneReaderError::new(ErrorKind::FormErr, &format!("invalid direction for record type {}", self.get_type())))
                };

                let val = (sign * (self.longitude as i64)) + (1 << 31);
                self.longitude = val as u32
            }
            8 => {
                let clean = value.trim_end_matches('m');
                self.altitude = (clean.parse::<f64>()
                    .map_err(|_| ZoneReaderError::new(ErrorKind::FormErr, &format!("unable to parse altitude param for record type {}", self.get_type())))? * 100.0).round() as u32;
            }
            9 => self.size = encode_loc_precision(value).map_err(|e| ZoneReaderError::new(ErrorKind::FormErr, &e.to_string()))?,
            10 => self.h_precision = encode_loc_precision(value).map_err(|e| ZoneReaderError::new(ErrorKind::FormErr, &e.to_string()))?,
            11 => self.v_precision = encode_loc_precision(value).map_err(|e| ZoneReaderError::new(ErrorKind::FormErr, &e.to_string()))?,
            _ => return Err(ZoneReaderError::new(ErrorKind::ExtraRRData, &format!("extra record data found for record type {}", self.get_type())))
        })
    }

    fn upcast(self) -> Box<dyn ZoneRRData> {
        Box::new(self)
    }
}

impl fmt::Display for LocRRData {

    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let (lat_deg, lat_min, lat_sec, lat_dir) = self.latitude.to_coord(true);
        let (lon_deg, lon_min, lon_sec, lon_dir) = self.longitude.to_coord(false);
        let alt = (self.altitude as f64 - 100_000.0 * 100.0) / 100.0;

        write!(f, "{:<8}{} {} {:.3} {} {} {} {:.3} {} {:.2}m {:.2}m {:.2}m {:.2}m", self.get_type().to_string(),
               lat_deg,
               lat_min,
               lat_sec,
               lat_dir,
               lon_deg,
               lon_min,
               lon_sec,
               lon_dir,
               alt,
               self.size as f64 / 100.0,
               self.h_precision as f64 / 100.0,
               self.v_precision as f64 / 100.0)
    }
}

#[test]
fn test() {
    let buf = vec![ 0x0, 0x10, 0x0, 0x0, 0x0, 0x0, 0x6e, 0x67, 0x2d, 0xa0, 0x9c, 0xf7, 0xc5, 0x80, 0x0, 0x0, 0x0, 0x0 ];
    let record = LocRRData::from_bytes(&buf, 0).unwrap();
    assert_eq!(buf, record.to_bytes().unwrap());
}