las 0.8.0

Read and write point clouds stored in the ASPRS las file format.
Documentation
//! Variable length records, both extended and regular.

use std::io::{Read, Write};
use crate::Result;

/// A raw variable length record.
#[derive(Debug, Default, PartialEq)]
pub struct Vlr {
    /// This value must be set to zero
    pub reserved: u16,

    /// The User ID field is ASCII character data that identifies the user which created the
    /// variable length record.
    ///
    /// It is possible to have many Variable Length Records from different sources with different
    /// User IDs. If the character data is less than 16 characters, the remaining data must be
    /// null. The User ID must be registered with the LAS specification managing body. The
    /// management of these User IDs ensures that no two individuals accidentally use the same User
    /// ID.
    pub user_id: [u8; 16],

    /// The Record ID is dependent upon the User ID.
    ///
    /// There can be 0 to 65,535 Record IDs for every User ID. The LAS specification manages its
    /// own Record IDs (User IDs owned by the specification), otherwise Record IDs will be managed
    /// by the owner of the given User ID. Thus each User ID is allowed to assign 0 to 65,535
    /// Record IDs in any manner they desire. Publicizing the meaning of a given Record ID is left
    /// to the owner of the given User ID. Unknown User ID/Record ID combinations should be
    /// ignored.
    pub record_id: u16,

    /// The record length is the number of bytes for the record after the end of the standard part
    /// of the header.
    ///
    /// Thus the entire record length is 54 bytes (the header size of the VLR) plus the number of
    /// bytes in the variable length portion of the record.
    pub record_length_after_header: RecordLength,

    /// Optional, null terminated text description of the data.
    ///
    /// Any remaining characters not used must be null.
    pub description: [u8; 32],

    #[allow(missing_docs)]
    pub data: Vec<u8>,
}

/// The length of the data in the vlr.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RecordLength {
    /// Vlrs use u16.
    Vlr(u16),
    /// Evlrs use u64.
    Evlr(u64),
}

impl Vlr {
    /// Reads a raw VLR or EVLR.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::{Seek, SeekFrom};
    /// use std::fs::File;
    /// use las::raw::Vlr;
    /// let mut file = File::open("tests/data/autzen.las").unwrap();
    /// file.seek(SeekFrom::Start(227));
    /// // If the second parameter were true, it would be read as an extended vlr.
    /// let vlr = Vlr::read_from(file, false).unwrap();
    /// ```
    #[allow(clippy::field_reassign_with_default)]
    pub fn read_from<R: Read>(mut read: R, extended: bool) -> Result<Vlr> {
        use byteorder::{LittleEndian, ReadBytesExt};

        let mut vlr = Vlr::default();
        vlr.reserved = read.read_u16::<LittleEndian>()?;
        read.read_exact(&mut vlr.user_id)?;
        vlr.record_id = read.read_u16::<LittleEndian>()?;
        vlr.record_length_after_header = if extended {
            RecordLength::Evlr(read.read_u64::<LittleEndian>()?)
        } else {
            RecordLength::Vlr(read.read_u16::<LittleEndian>()?)
        };
        read.read_exact(&mut vlr.description)?;
        vlr.data
            .resize(usize::from(vlr.record_length_after_header), 0);
        read.read_exact(&mut vlr.data)?;
        Ok(vlr)
    }

    /// Writes a raw VLR.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::Cursor;
    /// use las::raw::Vlr;
    /// let mut cursor = Cursor::new(Vec::new());
    /// let vlr = Vlr::default();
    /// vlr.write_to(cursor).unwrap();
    /// ```
    pub fn write_to<W: Write>(&self, mut write: W) -> Result<()> {
        use byteorder::{LittleEndian, WriteBytesExt};
        write.write_u16::<LittleEndian>(self.reserved)?;
        write.write_all(&self.user_id)?;
        write.write_u16::<LittleEndian>(self.record_id)?;
        match self.record_length_after_header {
            RecordLength::Vlr(n) => write.write_u16::<LittleEndian>(n)?,
            RecordLength::Evlr(n) => write.write_u64::<LittleEndian>(n)?,
        }
        write.write_all(&self.description)?;
        write.write_all(&self.data)?;
        Ok(())
    }
}

impl From<RecordLength> for u64 {
    fn from(record_length: RecordLength) -> u64 {
        match record_length {
            RecordLength::Vlr(n) => u64::from(n),
            RecordLength::Evlr(n) => n,
        }
    }
}

impl From<RecordLength> for usize {
    fn from(record_length: RecordLength) -> usize {
        u64::from(record_length) as usize
    }
}

impl Default for RecordLength {
    fn default() -> RecordLength {
        RecordLength::Vlr(0)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;

    #[test]
    fn roundtrip() {
        let vlr = Vlr::default();
        let mut cursor = Cursor::new(Vec::new());
        vlr.write_to(&mut cursor).unwrap();
        cursor.set_position(0);
        assert_eq!(vlr, Vlr::read_from(cursor, false).unwrap());
    }

    #[test]
    fn roundtrip_evlr() {
        let evlr = Vlr {
            record_length_after_header: RecordLength::Evlr(0),
            ..Default::default()
        };
        let mut cursor = Cursor::new(Vec::new());
        evlr.write_to(&mut cursor).unwrap();
        cursor.set_position(0);
        assert_eq!(evlr, Vlr::read_from(cursor, true).unwrap());
    }
}