<commit_before>use std::collections::HashMap;
use std::fmt;

/// Data associated with a single metadata block.
#[derive(Debug)]
pub struct Metadata {
  /// Marks whether the current metadata block is the last.
  is_last: bool,
  /// The length, in bytes, of the block being parsed. This does not include
  /// the metadata block header.
  length: u32,
  /// Block data containing one of the eight different types of metadata.
  pub data: Data,
}

/// An enum that represents a metadata block type.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Type {
  /// Represents the current block is stream information.
  StreamInfo,
  /// Represents the current block is padding.
  Padding,
  /// Represents the current block is application data.
  Application,
  /// Represents the current block is a seek table.
  SeekTable,
  /// Represents the current block is a vorbis comment.
  VorbisComment,
  /// Represents the current block is a cue sheet.
  CueSheet,
  /// Represents the current block is a picture.
  Picture,
  /// Represents the current block is unknow.
  Unknown,
}

macro_rules! is_block_type (
  ($name: ident, $block_type: ident) => (
    #[inline]
    pub fn $name(&self) -> bool {
      self.data_type() == Type::$block_type
    }
  );
);

impl Metadata {
  pub fn new(is_last: bool, length: u32, data: Data) -> Self {
    Metadata {
      is_last: is_last,
      length: length,
      data: data,
    }
  }

  /// Returns whether the current metadata block is the last.
  #[inline]
  pub fn is_last(&self) -> bool {
    self.is_last
  }

  pub fn data_type(&self) -> Type {
    match self.data {
      Data::StreamInfo(_)    => Type::StreamInfo,
      Data::Padding(_)       => Type::Padding,
      Data::Application(_)   => Type::Application,
      Data::SeekTable(_)     => Type::SeekTable,
      Data::VorbisComment(_) => Type::VorbisComment,
      Data::CueSheet(_)      => Type::CueSheet,
      Data::Picture(_)       => Type::Picture,
      Data::Unknown(_)       => Type::Unknown,
    }
  }

  is_block_type!(is_stream_info, StreamInfo);
  is_block_type!(is_padding, Padding);
  is_block_type!(is_application, Application);
  is_block_type!(is_seek_table, SeekTable);
  is_block_type!(is_vorbis_comment, VorbisComment);
  is_block_type!(is_cue_sheet, CueSheet);
  is_block_type!(is_picture, Picture);
  is_block_type!(is_unknown, Unknown);
}

/// General enum that hold all the different metadata block data.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// Information regarding the entire audio stream.
  StreamInfo(StreamInfo),
  /// Block that represents a number of padded bytes.
  Padding(u32),
  /// Data used by third-party applications.
  Application(Application),
  /// Table of multiple points to seek, or skip, to within the FLAC file.
  SeekTable(Vec<SeekPoint>),
  /// Stores human-readable name/value pairs.
  VorbisComment(VorbisComment),
  /// Stores cue information
  CueSheet(CueSheet),
  /// Stores pictures associated with the FLAC file.
  Picture(Picture),
  /// A type of block data that isn't know or doesn't match the type above.
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamInfo {
  /// Minimum block size, in samples, used in the stream.
  pub min_block_size: u16,
  /// Maximum block size, in samples, used in the stream.
  pub max_block_size: u16,
  /// Minimum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub min_frame_size: u32,
  /// Maximum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub max_frame_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels. FLAC supports one to eight channel.
  pub channels: u8,
  /// Bits per sample. FLAC supports four to thirty-two bits per sample.
  pub bits_per_sample: u8,
  /// Total samples in the stream. A value of zero means the number is
  /// unknown.
  pub total_samples: u64,
  /// MD5 signature of the unencoded audio data.
  pub md5_sum: [u8; 16],
}

impl StreamInfo {
  pub fn new() -> StreamInfo {
    StreamInfo {
      min_block_size: 0,
      max_block_size: 0,
      min_frame_size: 0,
      max_frame_size: 0,
      sample_rate: 0,
      channels: 0,
      bits_per_sample: 0,
      total_samples: 0,
      md5_sum: [0; 16],
    }
  }

  #[inline]
  pub fn is_varied_block_size(&self) -> bool {
    self.min_block_size != self.max_block_size
  }

  #[inline]
  pub fn is_fixed_block_size(&self) -> bool {
    self.min_block_size == self.max_block_size
  }
}

/// Data used by third-party applications.
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  /// Registered application ID.
  pub id: String,
  /// Data used by the third-party application.
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file.
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  /// Sample number of the first sample in the target frame.
  pub sample_number: u64,
  /// Byte offset of the target frame's header.
  pub stream_offset: u64,
  /// Number of samples in the target frame.
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs.
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  /// Vendor name.
  pub vendor_string: String,
  /// Comments associated with a name, or category, followed by it's
  /// contents.
  pub comments: HashMap<String, String>,
}

/// Stores cue information.
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  /// Media catalog number.
  pub media_catalog_number: String,
  /// Number of lead-in samples.
  pub lead_in: u64,
  /// Whether or not this `CueSheet` corresponds to a Compact Disc.
  pub is_cd: bool,
  /// One or more tracks.
  pub tracks: Vec<CueSheetTrack>,
}

/// Track information inside a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  /// Track offset, in samples, relative to the beginning of the FLAC audio
  /// stream.
  pub offset: u64,
  /// Track number.
  pub number: u8,
  /// Twelve digit alphanumeric code.
  pub isrc: String,
  /// Whether the cue sheet track is audio.
  pub is_audio: bool,
  /// Whether the cue sheet track is pre-emphasis.
  pub is_pre_emphasis: bool,
  /// For all tracks except the lead-out track.
  pub indices: Vec<CueSheetTrackIndex>,
}

/// An index point within a track, inside of a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  /// Offset, in samples, relative to the track offset of the index point.
  pub offset: u64,
  /// Index point number.
  pub number: u8,
}

/// Stores pictures associated with the FLAC file.
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  /// Picture type, based on the ID3v2 APIC frame.
  pub picture_type: PictureType,
  /// Multipurpose Internet Mail Extensions (MIME) type.
  pub mime_type: String,
  /// A string describing the picture.
  pub description: String,
  /// Width of the picture in pixels.
  pub width: u32,
  /// Height of the picture in pixels.
  pub height: u32,
  /// Color depth of the picture in bits-per-pixel.
  pub depth: u32,
  /// Number of colors used.
  pub colors: u32,
  /// Binary picture data.
  pub data: Vec<u8>,
}

/// The picture type according to the ID3v2 attached picture frame.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PictureType {
  /// Other picture type not categorized in this enum.
  Other,
  /// 32x32 pixels 'file icon'.
  FileIconStandard,
  /// Other, or non-standard, file icon.
  FileIcon,
  /// Cover (front).
  FrontCover,
  /// Cover (back).
  BackCover,
  /// Leaflet page.
  LeafletPage,
  /// Media, like label side of a CD.
  Media,
  /// Lead artist, lead performer, or soloist.
  LeadArtist,
  /// Artist or performer.
  Artist,
  /// Conductor.
  Conductor,
  /// Band or orchestra.
  Band,
  /// Composer.
  Composer,
  /// Lyricist or text writer.
  Lyricist,
  /// Recording location.
  RecordingLocation,
  /// During recording.
  DuringRecording,
  /// During performance.
  DuringPerformace,
  /// Movie, or video, screen capture.
  VideoScreenCapture,
  /// A bright colored fish.
  Fish,
  /// Illustration.
  Illustration,
  /// Band, or artist, logotype.
  BandLogo,
  /// Publisher, or studio, logotype.
  PublisherLogo,
}

impl fmt::Display for PictureType {
  fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    write!(formatter, "{}", match *self {
      PictureType::Other              => "Other",
      PictureType::FileIconStandard   => "File Icon (standard)",
      PictureType::FileIcon           => "File Icon",
      PictureType::FrontCover         => "Cover (front)",
      PictureType::BackCover          => "Cover (back)",
      PictureType::LeafletPage        => "Leaflet Page",
      PictureType::Media              => "Media",
      PictureType::LeadArtist         => "Lead Artist",
      PictureType::Artist             => "Arist",
      PictureType::Conductor          => "Conductor",
      PictureType::Band               => "Band",
      PictureType::Composer           => "Composer",
      PictureType::Lyricist           => "Lyricist",
      PictureType::RecordingLocation  => "Recoding Location",
      PictureType::DuringRecording    => "During Recording",
      PictureType::DuringPerformace   => "During Performace",
      PictureType::VideoScreenCapture => "Video Screen Capture",
      PictureType::Fish               => "Fish",
      PictureType::Illustration       => "Illustration",
      PictureType::BandLogo           => "Band Logo",
      PictureType::PublisherLogo      => "Publisher Logo",
    })
  }
}

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

  #[test]
  fn test_is_varied_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 1024;

    assert!(info.is_varied_block_size());

    info.min_block_size = 2048;
    info.max_block_size = 2048;

    assert!(!info.is_varied_block_size());
  }

  #[test]
  fn test_is_fixed_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 512;

    assert!(info.is_fixed_block_size());

    info.min_block_size = 1024;
    info.max_block_size = 2048;

    assert!(!info.is_fixed_block_size());
  }
}
<commit_msg>Add documentation for `Metadata::new`<commit_after>use std::collections::HashMap;
use std::fmt;

/// Data associated with a single metadata block.
#[derive(Debug)]
pub struct Metadata {
  /// Marks whether the current metadata block is the last.
  is_last: bool,
  /// The length, in bytes, of the block being parsed. This does not include
  /// the metadata block header.
  length: u32,
  /// Block data containing one of the eight different types of metadata.
  pub data: Data,
}

/// An enum that represents a metadata block type.
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Type {
  /// Represents the current block is stream information.
  StreamInfo,
  /// Represents the current block is padding.
  Padding,
  /// Represents the current block is application data.
  Application,
  /// Represents the current block is a seek table.
  SeekTable,
  /// Represents the current block is a vorbis comment.
  VorbisComment,
  /// Represents the current block is a cue sheet.
  CueSheet,
  /// Represents the current block is a picture.
  Picture,
  /// Represents the current block is unknow.
  Unknown,
}

macro_rules! is_block_type (
  ($name: ident, $block_type: ident) => (
    #[inline]
    pub fn $name(&self) -> bool {
      self.data_type() == Type::$block_type
    }
  );
);

impl Metadata {
  /// Constructs a new `Metadata` struct based on the arguments passed in.
  pub fn new(is_last: bool, length: u32, data: Data) -> Self {
    Metadata {
      is_last: is_last,
      length: length,
      data: data,
    }
  }

  /// Returns whether the current metadata block is the last.
  #[inline]
  pub fn is_last(&self) -> bool {
    self.is_last
  }

  pub fn data_type(&self) -> Type {
    match self.data {
      Data::StreamInfo(_)    => Type::StreamInfo,
      Data::Padding(_)       => Type::Padding,
      Data::Application(_)   => Type::Application,
      Data::SeekTable(_)     => Type::SeekTable,
      Data::VorbisComment(_) => Type::VorbisComment,
      Data::CueSheet(_)      => Type::CueSheet,
      Data::Picture(_)       => Type::Picture,
      Data::Unknown(_)       => Type::Unknown,
    }
  }

  is_block_type!(is_stream_info, StreamInfo);
  is_block_type!(is_padding, Padding);
  is_block_type!(is_application, Application);
  is_block_type!(is_seek_table, SeekTable);
  is_block_type!(is_vorbis_comment, VorbisComment);
  is_block_type!(is_cue_sheet, CueSheet);
  is_block_type!(is_picture, Picture);
  is_block_type!(is_unknown, Unknown);
}

/// General enum that hold all the different metadata block data.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// Information regarding the entire audio stream.
  StreamInfo(StreamInfo),
  /// Block that represents a number of padded bytes.
  Padding(u32),
  /// Data used by third-party applications.
  Application(Application),
  /// Table of multiple points to seek, or skip, to within the FLAC file.
  SeekTable(Vec<SeekPoint>),
  /// Stores human-readable name/value pairs.
  VorbisComment(VorbisComment),
  /// Stores cue information
  CueSheet(CueSheet),
  /// Stores pictures associated with the FLAC file.
  Picture(Picture),
  /// A type of block data that isn't know or doesn't match the type above.
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamInfo {
  /// Minimum block size, in samples, used in the stream.
  pub min_block_size: u16,
  /// Maximum block size, in samples, used in the stream.
  pub max_block_size: u16,
  /// Minimum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub min_frame_size: u32,
  /// Maximum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub max_frame_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels. FLAC supports one to eight channel.
  pub channels: u8,
  /// Bits per sample. FLAC supports four to thirty-two bits per sample.
  pub bits_per_sample: u8,
  /// Total samples in the stream. A value of zero means the number is
  /// unknown.
  pub total_samples: u64,
  /// MD5 signature of the unencoded audio data.
  pub md5_sum: [u8; 16],
}

impl StreamInfo {
  pub fn new() -> StreamInfo {
    StreamInfo {
      min_block_size: 0,
      max_block_size: 0,
      min_frame_size: 0,
      max_frame_size: 0,
      sample_rate: 0,
      channels: 0,
      bits_per_sample: 0,
      total_samples: 0,
      md5_sum: [0; 16],
    }
  }

  #[inline]
  pub fn is_varied_block_size(&self) -> bool {
    self.min_block_size != self.max_block_size
  }

  #[inline]
  pub fn is_fixed_block_size(&self) -> bool {
    self.min_block_size == self.max_block_size
  }
}

/// Data used by third-party applications.
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  /// Registered application ID.
  pub id: String,
  /// Data used by the third-party application.
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file.
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  /// Sample number of the first sample in the target frame.
  pub sample_number: u64,
  /// Byte offset of the target frame's header.
  pub stream_offset: u64,
  /// Number of samples in the target frame.
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs.
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  /// Vendor name.
  pub vendor_string: String,
  /// Comments associated with a name, or category, followed by it's
  /// contents.
  pub comments: HashMap<String, String>,
}

/// Stores cue information.
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  /// Media catalog number.
  pub media_catalog_number: String,
  /// Number of lead-in samples.
  pub lead_in: u64,
  /// Whether or not this `CueSheet` corresponds to a Compact Disc.
  pub is_cd: bool,
  /// One or more tracks.
  pub tracks: Vec<CueSheetTrack>,
}

/// Track information inside a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  /// Track offset, in samples, relative to the beginning of the FLAC audio
  /// stream.
  pub offset: u64,
  /// Track number.
  pub number: u8,
  /// Twelve digit alphanumeric code.
  pub isrc: String,
  /// Whether the cue sheet track is audio.
  pub is_audio: bool,
  /// Whether the cue sheet track is pre-emphasis.
  pub is_pre_emphasis: bool,
  /// For all tracks except the lead-out track.
  pub indices: Vec<CueSheetTrackIndex>,
}

/// An index point within a track, inside of a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  /// Offset, in samples, relative to the track offset of the index point.
  pub offset: u64,
  /// Index point number.
  pub number: u8,
}

/// Stores pictures associated with the FLAC file.
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  /// Picture type, based on the ID3v2 APIC frame.
  pub picture_type: PictureType,
  /// Multipurpose Internet Mail Extensions (MIME) type.
  pub mime_type: String,
  /// A string describing the picture.
  pub description: String,
  /// Width of the picture in pixels.
  pub width: u32,
  /// Height of the picture in pixels.
  pub height: u32,
  /// Color depth of the picture in bits-per-pixel.
  pub depth: u32,
  /// Number of colors used.
  pub colors: u32,
  /// Binary picture data.
  pub data: Vec<u8>,
}

/// The picture type according to the ID3v2 attached picture frame.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PictureType {
  /// Other picture type not categorized in this enum.
  Other,
  /// 32x32 pixels 'file icon'.
  FileIconStandard,
  /// Other, or non-standard, file icon.
  FileIcon,
  /// Cover (front).
  FrontCover,
  /// Cover (back).
  BackCover,
  /// Leaflet page.
  LeafletPage,
  /// Media, like label side of a CD.
  Media,
  /// Lead artist, lead performer, or soloist.
  LeadArtist,
  /// Artist or performer.
  Artist,
  /// Conductor.
  Conductor,
  /// Band or orchestra.
  Band,
  /// Composer.
  Composer,
  /// Lyricist or text writer.
  Lyricist,
  /// Recording location.
  RecordingLocation,
  /// During recording.
  DuringRecording,
  /// During performance.
  DuringPerformace,
  /// Movie, or video, screen capture.
  VideoScreenCapture,
  /// A bright colored fish.
  Fish,
  /// Illustration.
  Illustration,
  /// Band, or artist, logotype.
  BandLogo,
  /// Publisher, or studio, logotype.
  PublisherLogo,
}

impl fmt::Display for PictureType {
  fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
    write!(formatter, "{}", match *self {
      PictureType::Other              => "Other",
      PictureType::FileIconStandard   => "File Icon (standard)",
      PictureType::FileIcon           => "File Icon",
      PictureType::FrontCover         => "Cover (front)",
      PictureType::BackCover          => "Cover (back)",
      PictureType::LeafletPage        => "Leaflet Page",
      PictureType::Media              => "Media",
      PictureType::LeadArtist         => "Lead Artist",
      PictureType::Artist             => "Arist",
      PictureType::Conductor          => "Conductor",
      PictureType::Band               => "Band",
      PictureType::Composer           => "Composer",
      PictureType::Lyricist           => "Lyricist",
      PictureType::RecordingLocation  => "Recoding Location",
      PictureType::DuringRecording    => "During Recording",
      PictureType::DuringPerformace   => "During Performace",
      PictureType::VideoScreenCapture => "Video Screen Capture",
      PictureType::Fish               => "Fish",
      PictureType::Illustration       => "Illustration",
      PictureType::BandLogo           => "Band Logo",
      PictureType::PublisherLogo      => "Publisher Logo",
    })
  }
}

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

  #[test]
  fn test_is_varied_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 1024;

    assert!(info.is_varied_block_size());

    info.min_block_size = 2048;
    info.max_block_size = 2048;

    assert!(!info.is_varied_block_size());
  }

  #[test]
  fn test_is_fixed_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 512;

    assert!(info.is_fixed_block_size());

    info.min_block_size = 1024;
    info.max_block_size = 2048;

    assert!(!info.is_fixed_block_size());
  }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

/// Data associated with a single metadata block.
#[derive(Debug)]
pub struct Metadata {
  /// Marks whether the current metadata block is the last.
  pub is_last: bool,
  /// The length, in bytes, of the block being parsed. This does not include
  /// the metadata block header.
  pub length: u32,
  /// Block data containing one of the eight different types of metadata.
  pub data: Data,
}

/// General enum that hold all the different metadata block data.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// Information regarding the entire audio stream.
  StreamInfo(StreamInfo),
  /// Block that represents a number of padded bytes.
  Padding(u32),
  /// Data used by third-party applications.
  Application(Application),
  /// Table of multiple points to seek, or skip, to within the FLAC file.
  SeekTable(Vec<SeekPoint>),
  /// Stores human-readable name/value pairs.
  VorbisComment(VorbisComment),
  /// Stores cue information
  CueSheet(CueSheet),
  /// Stores pictures associated with the FLAC file.
  Picture(Picture),
  /// A type of block data that isn't know or doesn't match the type above.
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamInfo {
  /// Minimum block size, in samples, used in the stream.
  pub min_block_size: u16,
  /// Maximum block size, in samples, used in the stream.
  pub max_block_size: u16,
  /// Minimum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub min_frame_size: u32,
  /// Maximum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub max_frame_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels. FLAC supports one to eight channel.
  pub channels: u8,
  /// Bits per sample. FLAC supports four to thirty-two bits per sample.
  pub bits_per_sample: u8,
  /// Total samples in the stream. A value of zero means the number is
  /// unknown.
  pub total_samples: u64,
  /// MD5 signature of the unencoded audio data.
  pub md5_sum: [u8; 16],
}

impl StreamInfo {
  pub fn new() -> StreamInfo {
    StreamInfo {
      min_block_size: 0,
      max_block_size: 0,
      min_frame_size: 0,
      max_frame_size: 0,
      sample_rate: 0,
      channels: 0,
      bits_per_sample: 0,
      total_samples: 0,
      md5_sum: [0; 16],
    }
  }

  #[inline]
  pub fn is_varied_block_size(&self) -> bool {
    self.min_block_size != self.max_block_size
  }

  #[inline]
  pub fn is_fixed_block_size(&self) -> bool {
    self.min_block_size == self.max_block_size
  }
}

/// Data used by third-party applications.
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  /// Registered application ID.
  pub id: String,
  /// Data used by the third-party application.
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file.
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  /// Sample number of the first sample in the target frame.
  pub sample_number: u64,
  /// Byte offset of the target frame's header.
  pub stream_offset: u64,
  /// Number of samples in the target frame.
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs.
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  /// Vendor name.
  pub vendor_string: String,
  /// Comments associated with a name, or category, followed by it's
  /// contents.
  pub comments: HashMap<String, String>,
}

/// Stores cue information.
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  /// Media catalog number.
  pub media_catalog_number: String,
  /// Number of lead-in samples.
  pub lead_in: u64,
  /// Whether or not this `CueSheet` corresponds to a Compact Disc.
  pub is_cd: bool,
  /// One or more tracks.
  pub tracks: Vec<CueSheetTrack>,
}

/// Track information inside a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  /// Track offset, in samples, relative to the beginning of the FLAC audio
  /// stream.
  pub offset: u64,
  /// Track number.
  pub number: u8,
  /// Twelve digit alphanumeric code.
  pub isrc: String,
  /// Whether the cue sheet track is audio.
  pub is_audio: bool,
  /// Whether the cue sheet track is pre-emphasis.
  pub is_pre_emphasis: bool,
  /// For all tracks except the lead-out track.
  pub indices: Vec<CueSheetTrackIndex>,
}

/// An index point within a track, inside of a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  /// Offset, in samples, relative to the track offset of the index point.
  pub offset: u64,
  /// Index point number.
  pub number: u8,
}

/// Stores pictures associated with the FLAC file.
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  /// Picture type, based on the ID3v2 APIC frame.
  pub picture_type: PictureType,
  /// Multipurpose Internet Mail Extensions (MIME) type.
  pub mime_type: String,
  /// A string describing the picture.
  pub description: String,
  /// Width of the picture in pixels.
  pub width: u32,
  /// Height of the picture in pixels.
  pub height: u32,
  /// Color depth of the picture in bits-per-pixel.
  pub depth: u32,
  /// Number of colors used.
  pub colors: u32,
  /// Binary picture data.
  pub data: Vec<u8>,
}

/// The picture type according to the ID3v2 attached picture frame.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PictureType {
  /// Other picture type not categorized in this enum.
  Other,
  /// 32x32 pixels 'file icon'.
  FileIconStandard,
  /// Other, or non-standard, file icon.
  FileIcon,
  /// Cover (front).
  FrontCover,
  /// Cover (back).
  BackCover,
  /// Leaflet page.
  LeafletPage,
  /// Media, like label side of a CD.
  Media,
  /// Lead artist, lead performer, or soloist.
  LeadArtist,
  /// Artist or performer.
  Artist,
  /// Conductor.
  Conductor,
  /// Band or orchestra.
  Band,
  /// Composer.
  Composer,
  /// Lyricist or text writer.
  Lyricist,
  /// Recording location.
  RecordingLocation,
  /// During recording.
  DuringRecording,
  /// During performance.
  DuringPerformace,
  /// Movie, or video, screen capture.
  VideoScreenCapture,
  /// A bright colored fish.
  Fish,
  /// Illustration.
  Illustration,
  /// Band, or artist, logotype.
  BandLogo,
  /// Publisher, or studio, logotype.
  PublisherLogo,
}
<commit_msg>Add test for `StreamInfo::is_varied_block_size`<commit_after>use std::collections::HashMap;

/// Data associated with a single metadata block.
#[derive(Debug)]
pub struct Metadata {
  /// Marks whether the current metadata block is the last.
  pub is_last: bool,
  /// The length, in bytes, of the block being parsed. This does not include
  /// the metadata block header.
  pub length: u32,
  /// Block data containing one of the eight different types of metadata.
  pub data: Data,
}

/// General enum that hold all the different metadata block data.
#[derive(Debug, PartialEq, Eq)]
pub enum Data {
  /// Information regarding the entire audio stream.
  StreamInfo(StreamInfo),
  /// Block that represents a number of padded bytes.
  Padding(u32),
  /// Data used by third-party applications.
  Application(Application),
  /// Table of multiple points to seek, or skip, to within the FLAC file.
  SeekTable(Vec<SeekPoint>),
  /// Stores human-readable name/value pairs.
  VorbisComment(VorbisComment),
  /// Stores cue information
  CueSheet(CueSheet),
  /// Stores pictures associated with the FLAC file.
  Picture(Picture),
  /// A type of block data that isn't know or doesn't match the type above.
  Unknown(Vec<u8>),
}

/// Information regarding the entire audio stream.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct StreamInfo {
  /// Minimum block size, in samples, used in the stream.
  pub min_block_size: u16,
  /// Maximum block size, in samples, used in the stream.
  pub max_block_size: u16,
  /// Minimum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub min_frame_size: u32,
  /// Maximum frame size, in bytes, used in the stream. May be zero to imply
  /// the value isn't know.
  pub max_frame_size: u32,
  /// Sample rate in hertz (Hz).
  pub sample_rate: u32,
  /// Number of channels. FLAC supports one to eight channel.
  pub channels: u8,
  /// Bits per sample. FLAC supports four to thirty-two bits per sample.
  pub bits_per_sample: u8,
  /// Total samples in the stream. A value of zero means the number is
  /// unknown.
  pub total_samples: u64,
  /// MD5 signature of the unencoded audio data.
  pub md5_sum: [u8; 16],
}

impl StreamInfo {
  pub fn new() -> StreamInfo {
    StreamInfo {
      min_block_size: 0,
      max_block_size: 0,
      min_frame_size: 0,
      max_frame_size: 0,
      sample_rate: 0,
      channels: 0,
      bits_per_sample: 0,
      total_samples: 0,
      md5_sum: [0; 16],
    }
  }

  #[inline]
  pub fn is_varied_block_size(&self) -> bool {
    self.min_block_size != self.max_block_size
  }

  #[inline]
  pub fn is_fixed_block_size(&self) -> bool {
    self.min_block_size == self.max_block_size
  }
}

/// Data used by third-party applications.
#[derive(Debug, PartialEq, Eq)]
pub struct Application {
  /// Registered application ID.
  pub id: String,
  /// Data used by the third-party application.
  pub data: Vec<u8>,
}

/// Seek, or skip, to a point within the FLAC file.
#[derive(Debug, PartialEq, Eq)]
pub struct SeekPoint {
  /// Sample number of the first sample in the target frame.
  pub sample_number: u64,
  /// Byte offset of the target frame's header.
  pub stream_offset: u64,
  /// Number of samples in the target frame.
  pub frame_samples: u16,
}

/// Stores human-readable name/value pairs.
#[derive(Debug, PartialEq, Eq)]
pub struct VorbisComment {
  /// Vendor name.
  pub vendor_string: String,
  /// Comments associated with a name, or category, followed by it's
  /// contents.
  pub comments: HashMap<String, String>,
}

/// Stores cue information.
///
/// Generally for storing information from Compact Disk Digital Audio, but
/// can be used as a cueing mechanism for playback.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheet {
  /// Media catalog number.
  pub media_catalog_number: String,
  /// Number of lead-in samples.
  pub lead_in: u64,
  /// Whether or not this `CueSheet` corresponds to a Compact Disc.
  pub is_cd: bool,
  /// One or more tracks.
  pub tracks: Vec<CueSheetTrack>,
}

/// Track information inside a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrack {
  /// Track offset, in samples, relative to the beginning of the FLAC audio
  /// stream.
  pub offset: u64,
  /// Track number.
  pub number: u8,
  /// Twelve digit alphanumeric code.
  pub isrc: String,
  /// Whether the cue sheet track is audio.
  pub is_audio: bool,
  /// Whether the cue sheet track is pre-emphasis.
  pub is_pre_emphasis: bool,
  /// For all tracks except the lead-out track.
  pub indices: Vec<CueSheetTrackIndex>,
}

/// An index point within a track, inside of a cue sheet.
#[derive(Debug, PartialEq, Eq)]
pub struct CueSheetTrackIndex {
  /// Offset, in samples, relative to the track offset of the index point.
  pub offset: u64,
  /// Index point number.
  pub number: u8,
}

/// Stores pictures associated with the FLAC file.
///
/// More than likely these pictures will be cover art, but you can have more
/// than one within a file, which are distinguished by `PictureType`and it's
/// mime type string.
#[derive(Debug, PartialEq, Eq)]
pub struct Picture {
  /// Picture type, based on the ID3v2 APIC frame.
  pub picture_type: PictureType,
  /// Multipurpose Internet Mail Extensions (MIME) type.
  pub mime_type: String,
  /// A string describing the picture.
  pub description: String,
  /// Width of the picture in pixels.
  pub width: u32,
  /// Height of the picture in pixels.
  pub height: u32,
  /// Color depth of the picture in bits-per-pixel.
  pub depth: u32,
  /// Number of colors used.
  pub colors: u32,
  /// Binary picture data.
  pub data: Vec<u8>,
}

/// The picture type according to the ID3v2 attached picture frame.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PictureType {
  /// Other picture type not categorized in this enum.
  Other,
  /// 32x32 pixels 'file icon'.
  FileIconStandard,
  /// Other, or non-standard, file icon.
  FileIcon,
  /// Cover (front).
  FrontCover,
  /// Cover (back).
  BackCover,
  /// Leaflet page.
  LeafletPage,
  /// Media, like label side of a CD.
  Media,
  /// Lead artist, lead performer, or soloist.
  LeadArtist,
  /// Artist or performer.
  Artist,
  /// Conductor.
  Conductor,
  /// Band or orchestra.
  Band,
  /// Composer.
  Composer,
  /// Lyricist or text writer.
  Lyricist,
  /// Recording location.
  RecordingLocation,
  /// During recording.
  DuringRecording,
  /// During performance.
  DuringPerformace,
  /// Movie, or video, screen capture.
  VideoScreenCapture,
  /// A bright colored fish.
  Fish,
  /// Illustration.
  Illustration,
  /// Band, or artist, logotype.
  BandLogo,
  /// Publisher, or studio, logotype.
  PublisherLogo,
}

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

  #[test]
  fn test_is_varied_block_size() {
    let mut info = StreamInfo::new();

    info.min_block_size = 512;
    info.max_block_size = 1024;

    assert!(info.is_varied_block_size());

    info.min_block_size = 2048;
    info.max_block_size = 2048;

    assert!(!info.is_varied_block_size());
  }
}
<|endoftext|>


<commit_before>use heartbeats_simple_sys::pow::*;
use std::mem;
use std::io::{self, Write};
use std::fs::File;

/// Contains the Heartbeat and its window data buffer.
pub struct HeartbeatPow {
    pub hb: heartbeat_pow_context,
    pub hbr: Vec<heartbeat_pow_record>,
    pub log: Option<File>,
}

impl HeartbeatPow {
    /// Allocate and initialize a new `Heartbeat`.
    pub fn new(window_size: usize,
               hwc_callback: Option<heartbeat_pow_window_complete>,
               mut log: Option<File>) -> Result<HeartbeatPow, &'static str> {
        let mut hbr: Vec<heartbeat_pow_record> = Vec::with_capacity(window_size);
        unsafe {
            // must explicitly set size so we can read data later
            // (Rust isn't aware of native code modifying the buffer)
            hbr.set_len(window_size);
            let mut hb = mem::uninitialized();
            match heartbeat_pow_init(&mut hb,
                                     hbr.capacity() as u64,
                                     hbr.as_mut_ptr(),
                                     hwc_callback) {
                0 => {
                    if let Some(ref mut l) = log {
                        l.write_all("HB    Tag    Work    Start_Time    End_time    \
                            Global_Perf    Window_Perf    Instant_Perf    \
                            Start_Energy    End_Energy    \
                            Global_Pwr    Window_Pwr    Instant_Pwr\n".as_bytes()).unwrap()
                    }
                    Ok(HeartbeatPow { hb: hb, hbr: hbr, log: log, })
                },
                _ => Err("Failed to initialize heartbeat")
            }
        }
    }

    /// Issue a heartbeat
    pub fn heartbeat(&mut self,
                     tag: u64,
                     work: u64,
                     start_time: u64,
                     end_time: u64,
                     start_energy: u64,
                     end_energy: u64) {
        unsafe {
            heartbeat_pow(&mut self.hb, tag, work, start_time, end_time, start_energy, end_energy)
        }
    }

    fn write_log(r: &heartbeat_pow_record, l: &mut File) -> io::Result<usize> {
        l.write(format!("{}    {}    {}    {}    {}    \
                         {}    {}    {}    \
                         {}    {}    \
                         {}    {}    {}\n",
                        r.id, r.user_tag, r.work, r.start_time, r.end_time,
                        r.perf.global, r.perf.window, r.perf.instant,
                        r.start_energy, r.end_energy,
                        r.pwr.global, r.pwr.window, r.pwr.instant).as_bytes())
    }

    /// Rust-only function that logs the buffer (up to buffer_index) to a file.
    pub fn log_to_buffer_index(&mut self) -> io::Result<()> {
        match self.log {
            Some(ref mut l) => {
                for i in 0..self.hb.ws.buffer_index {
                    match HeartbeatPow::write_log(self.hbr.get(i as usize).unwrap(), l) {
                        Ok(_) => (),
                        Err(e) => return Err(e),
                    }
                }
                l.flush()
            }
            None => Ok(())
        }
    }
}

#[cfg(test)]
mod test {
    use super::HeartbeatPow;
    use pow::*;
    use std::fs::File;

    #[test]
    fn test_simple() {
        const TIME_INC: u64 = 1000000000;
        const ENERGY_INC: u64 = 1000000;
        let mut hb = HeartbeatPow::new(5, None, None).unwrap();
        let mut start_time: u64 = 0;
        let mut end_time: u64 = TIME_INC;
        let mut start_energy: u64 = 0;
        let mut end_energy: u64 = ENERGY_INC;
        for tag in 0..10 {
            hb.heartbeat(tag, 1, start_time, end_time, start_energy, end_energy);
            start_time = end_time;
            end_time += TIME_INC;
            start_energy = end_energy;
            end_energy += ENERGY_INC;
        }
    }

    #[test]
    fn test_file() {
        let mut hb = HeartbeatPow::new(5, None, Some(File::create("foo.log").unwrap())).unwrap();
        hb.heartbeat(0, 1, 0, 1, 0, 0);
        hb.log_to_buffer_index().unwrap();
    }

    #[test]
    fn test_callback() {
        let mut hb = HeartbeatPow::new(5, None, None).unwrap();
        hb.hb.hwc_callback = |h: *const heartbeat_pow_context| heartbeat_pow_log_window_buffer(h, 1, 0);
        hb.heartbeat(0, 1, 0, 1, 0, 0);
        hb.log_to_buffer_index().unwrap();
    }
}
<commit_msg>Fix tests<commit_after>use heartbeats_simple_sys::pow::*;
use std::mem;
use std::io::{self, Write};
use std::fs::File;

/// Contains the Heartbeat and its window data buffer.
pub struct HeartbeatPow {
    pub hb: heartbeat_pow_context,
    pub hbr: Vec<heartbeat_pow_record>,
    pub log: Option<File>,
}

impl HeartbeatPow {
    /// Allocate and initialize a new `Heartbeat`.
    pub fn new(window_size: usize,
               hwc_callback: Option<heartbeat_pow_window_complete>,
               mut log: Option<File>) -> Result<HeartbeatPow, &'static str> {
        let mut hbr: Vec<heartbeat_pow_record> = Vec::with_capacity(window_size);
        unsafe {
            // must explicitly set size so we can read data later
            // (Rust isn't aware of native code modifying the buffer)
            hbr.set_len(window_size);
            let mut hb = mem::uninitialized();
            match heartbeat_pow_init(&mut hb,
                                     hbr.capacity() as u64,
                                     hbr.as_mut_ptr(),
                                     hwc_callback) {
                0 => {
                    if let Some(ref mut l) = log {
                        l.write_all("HB    Tag    Work    Start_Time    End_time    \
                            Global_Perf    Window_Perf    Instant_Perf    \
                            Start_Energy    End_Energy    \
                            Global_Pwr    Window_Pwr    Instant_Pwr\n".as_bytes()).unwrap()
                    }
                    Ok(HeartbeatPow { hb: hb, hbr: hbr, log: log, })
                },
                _ => Err("Failed to initialize heartbeat")
            }
        }
    }

    /// Issue a heartbeat
    pub fn heartbeat(&mut self,
                     tag: u64,
                     work: u64,
                     start_time: u64,
                     end_time: u64,
                     start_energy: u64,
                     end_energy: u64) {
        unsafe {
            heartbeat_pow(&mut self.hb, tag, work, start_time, end_time, start_energy, end_energy)
        }
    }

    fn write_log(r: &heartbeat_pow_record, l: &mut File) -> io::Result<usize> {
        l.write(format!("{}    {}    {}    {}    {}    \
                         {}    {}    {}    \
                         {}    {}    \
                         {}    {}    {}\n",
                        r.id, r.user_tag, r.work, r.start_time, r.end_time,
                        r.perf.global, r.perf.window, r.perf.instant,
                        r.start_energy, r.end_energy,
                        r.pwr.global, r.pwr.window, r.pwr.instant).as_bytes())
    }

    /// Rust-only function that logs the buffer (up to buffer_index) to a file.
    pub fn log_to_buffer_index(&mut self) -> io::Result<()> {
        match self.log {
            Some(ref mut l) => {
                for i in 0..self.hb.ws.buffer_index {
                    match HeartbeatPow::write_log(self.hbr.get(i as usize).unwrap(), l) {
                        Ok(_) => (),
                        Err(e) => return Err(e),
                    }
                }
                l.flush()
            }
            None => Ok(())
        }
    }
}

#[cfg(test)]
mod test {
    use super::HeartbeatPow;
    use std::fs::File;

    #[test]
    fn test_simple() {
        const TIME_INC: u64 = 1000000000;
        const ENERGY_INC: u64 = 1000000;
        let mut hb = HeartbeatPow::new(5, None, None).unwrap();
        let mut start_time: u64 = 0;
        let mut end_time: u64 = TIME_INC;
        let mut start_energy: u64 = 0;
        let mut end_energy: u64 = ENERGY_INC;
        for tag in 0..10 {
            hb.heartbeat(tag, 1, start_time, end_time, start_energy, end_energy);
            start_time = end_time;
            end_time += TIME_INC;
            start_energy = end_energy;
            end_energy += ENERGY_INC;
        }
    }

    #[test]
    fn test_file() {
        let mut hb = HeartbeatPow::new(5, None, Some(File::create("foo.log").unwrap())).unwrap();
        hb.heartbeat(0, 1, 0, 1, 0, 0);
        hb.log_to_buffer_index().unwrap();
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 Emma Welker et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate postgres;
extern crate postgres_inet;

use postgres::{Connection, TlsMode};
use postgres_inet::MaskedIpAddr;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

struct Employee {
    name: String,
    network: MaskedIpAddr,
    workstation: IpAddr,
}

fn main() {
    let employees = vec![
        Employee {
            name: String::from("John Smith"),
            network: MaskedIpAddr::new(Ipv4Addr::new(10, 0, 0, 0), 8),
            workstation: IpAddr::V4(Ipv4Addr::new(203, 0, 113, 2)),
        },
        Employee {
            name: String::from("Jane Doe"),
            network: MaskedIpAddr::new(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0), 7),
            workstation: IpAddr::V6(Ipv6Addr::new(0x2001, 0x0DB8, 0, 0, 0, 0, 0, 0xC513)),
        },
    ];
    let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();

    conn.execute(
        "CREATE TABLE employees (
            id SERIAL PRIMARY KEY,
            name VARCHAR NOT NULL,
            network CIDR NOT NULL,
            workstation INET NOT NULL
        )",
        &[],
    ).unwrap();

    for employee in employees {
        let workstation: MaskedIpAddr = From::from(employee.workstation);
        conn.execute(
            "INSERT INTO employees (name, network, workstation) VALUES ($1, $2, $3)",
            &[&employee.name, &employee.network, &workstation],
        ).unwrap();
    }

    for row in &conn.query("SELECT name, network, workstation FROM employees", &[])
        .unwrap()
    {
        let workstation: MaskedIpAddr = row.get(2);
        let employee = Employee {
            name: row.get(0),
            network: row.get(1),
            workstation: From::from(workstation),
        };

        println!(
            "{} manages {} from {}",
            employee.name, employee.network, employee.workstation
        );
    }
    conn.execute("DROP TABLE employees", &[]).unwrap();
}
<commit_msg>Allow rustfmt to do its thing<commit_after>// Copyright 2017 Emma Welker et al.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate postgres;
extern crate postgres_inet;

use postgres::{Connection, TlsMode};
use postgres_inet::MaskedIpAddr;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

struct Employee {
    name: String,
    network: MaskedIpAddr,
    workstation: IpAddr,
}

fn main() {
    let employees = vec![
        Employee {
            name: String::from("John Smith"),
            network: MaskedIpAddr::new(Ipv4Addr::new(10, 0, 0, 0), 8),
            workstation: IpAddr::V4(Ipv4Addr::new(203, 0, 113, 2)),
        },
        Employee {
            name: String::from("Jane Doe"),
            network: MaskedIpAddr::new(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 0), 7),
            workstation: IpAddr::V6(Ipv6Addr::new(0x2001, 0x0DB8, 0, 0, 0, 0, 0, 0xC513)),
        },
    ];
    let conn = Connection::connect("postgres://postgres@localhost", TlsMode::None).unwrap();

    conn.execute(
        "CREATE TABLE employees (
            id SERIAL PRIMARY KEY,
            name VARCHAR NOT NULL,
            network CIDR NOT NULL,
            workstation INET NOT NULL
        )",
        &[],
    )
    .unwrap();

    for employee in employees {
        let workstation: MaskedIpAddr = From::from(employee.workstation);
        conn.execute(
            "INSERT INTO employees (name, network, workstation) VALUES ($1, $2, $3)",
            &[&employee.name, &employee.network, &workstation],
        )
        .unwrap();
    }

    for row in &conn
        .query("SELECT name, network, workstation FROM employees", &[])
        .unwrap()
    {
        let workstation: MaskedIpAddr = row.get(2);
        let employee = Employee {
            name: row.get(0),
            network: row.get(1),
            workstation: From::from(workstation),
        };

        println!(
            "{} manages {} from {}",
            employee.name, employee.network, employee.workstation
        );
    }
    conn.execute("DROP TABLE employees", &[]).unwrap();
}
<|endoftext|>


<commit_before>use math::{Point, Vector};

use cgmath::prelude::*;
use cgmath::ApproxEq;

#[derive(Copy, Clone)]
pub struct Ray {
    pub origin: Point,
    pub direction: Vector,
}

impl Ray {
    pub fn new(origin: Point, direction: Vector) -> Ray {
        debug_assert!(direction.magnitude().approx_eq(&1.0),
                      "Ray direction is not normalized. (|{:?}| == {:?} != 1.0)",
                      direction,
                      direction.magnitude());

        Ray {
            origin: origin,
            direction: direction,
        }
    }

    pub fn evaluate(&self, distance: f32) -> Point {
        self.origin + self.direction * distance
    }
}
<commit_msg>Add `Ray::reflect()`<commit_after>use math::{Point, Vector};

use cgmath::prelude::*;
use cgmath::{ApproxEq, dot};

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Ray {
    pub origin: Point,
    pub direction: Vector,
}

impl Ray {
    pub fn new(origin: Point, direction: Vector) -> Ray {
        debug_assert!(direction.magnitude().approx_eq(&1.0),
                      "Ray direction is not normalized. (|{:?}| == {:?} != 1.0)",
                      direction,
                      direction.magnitude());

        Ray {
            origin: origin,
            direction: direction,
        }
    }

    pub fn evaluate(&self, distance: f32) -> Point {
        self.origin + self.direction * distance
    }

    pub fn reflect(&self, normal: Vector, intersection: Point) -> Ray {
        let new_direction =
            self.direction - 2f32 * dot(self.direction, normal) * normal;

        Ray {
            origin: intersection,
            direction: new_direction,
        }
    }
}

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

    #[test]
    fn test_reflect() {
        let ray = Ray::new(vec3(-1.0, 1.0, 0.0), vec3(1.0, -1.0, 0.0).normalize());
        let normal = vec3(0.0, 1.0, 0.0);
        let intersection = vec3(0.0, 0.0, 0.0);

        let result = Ray::new(vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 0.0).normalize());
        assert_eq!(ray.reflect(normal, intersection), result);
    }
}
<|endoftext|>


<commit_before>use core::{fmt,mem};
use core::result::Result;
use syscalls::{self, allow};

use alloc::{String, VecDeque};

const DRIVER_NUM: u32 = 0;

pub struct Console {
    queue: VecDeque<String>,
    outstanding: Option<String>
}

impl Console {
    pub fn new() -> Console {
        Console {
            queue: VecDeque::new(),
            outstanding: None
        }
    }

    pub fn write(&mut self, string: String) {
        if self.outstanding.is_none() {
            unsafe {
                if putstr_async(&string, Self::cb, self as *const _ as usize) >= 0 {
                    self.outstanding = Some(string);
                }
            }
        } else {
            self.queue.push_back(string);
        }
    }

    extern fn cb(_: usize, _: usize, _: usize, ptr: usize) {
        let console: &mut Console = unsafe {
            mem::transmute(ptr)
        };
        console.outstanding.take();
        if let Some(next) = console.queue.pop_front() {
            unsafe {
                putstr_async(&next, Self::cb, ptr);
            }
            console.outstanding = Some(next);
        }
    }
}

impl fmt::Write for Console {
    fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> {
        self.write(String::from(string));
        Ok(())
    }
}

unsafe fn putstr_async(string: &String, cb: extern fn (usize, usize, usize, usize), ud: usize) -> isize {
  let mut ret = allow(DRIVER_NUM, 1, string.as_bytes());
  if ret < 0 {
      return ret;
  }

  ret = syscalls::subscribe(DRIVER_NUM, 1, cb, ud);
  if ret < 0 {
      return ret;
  }

  ret = syscalls::command(DRIVER_NUM, 1, string.len() as isize);
  if ret < 0 {
      return ret;
  }
  ret
}

<commit_msg>Just make console synchronous for now<commit_after>use core::cell::Cell;
use core::{fmt,mem};
use core::result::Result;
use syscalls::{self, allow, yieldk_for};

use alloc::String;

const DRIVER_NUM: u32 = 0;

pub struct Console;

impl Console {
    pub fn new() -> Console {
        Console
    }

    pub fn write(&mut self, string: String) {
        if string.len() <= 0 {
            return;
        }
        let done: Cell<bool> = Cell::new(false);
        unsafe {
            if putstr_async(&string, Self::cb, &done as *const _ as usize) >= 0 {
                yieldk_for(|| done.get())
            } else {
                return
            }
        }
    }

    extern fn cb(_: usize, _: usize, _: usize, ptr: usize) {
        let done: &Cell<bool> = unsafe {
            mem::transmute(ptr)
        };
        done.set(true);
    }
}

impl fmt::Write for Console {
    fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> {
        self.write(String::from(string));
        Ok(())
    }
}

unsafe fn putstr_async(string: &String, cb: extern fn (usize, usize, usize, usize), ud: usize) -> isize {
  let mut ret = allow(DRIVER_NUM, 1, string.as_bytes());
  if ret < 0 {
      return ret;
  }

  ret = syscalls::subscribe(DRIVER_NUM, 1, cb, ud);
  if ret < 0 {
      return ret;
  }

  ret = syscalls::command(DRIVER_NUM, 1, string.len() as isize);
  if ret < 0 {
      return ret;
  }
  ret
}

<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the file stat action.
//!
//! A file stat action collects filesystem metadata associated with a particular
//! file.
//!
//! Note that the gathered bits of information differ across platforms, e.g. on
//! Linux there is a notion of symlinks whereas on Windows no such thing exists.
//! Therefore, on Linux the results might include additional information about
//! the symlink (like the file it points to).

use std::fs::Metadata;
use std::path::PathBuf;

use log::warn;

use crate::session::{self, Session};

/// A request type for the stat action.
#[derive(Debug)]
pub struct Request {
    /// A path to the file to stat.
    path: PathBuf,
    /// Whether to collect extended file attributes.
    collect_ext_attrs: bool,
    /// Whether, in case of a symlink, to collect data about the linked file.
    follow_symlink: bool,
}

impl Request {

    /// Obtains a (potentially expanded) path that this request corresponds to.
    ///
    /// In case of requests that wish to follow symlinks, it will return a path
    /// to the symlink target (in case there is such). Otherwise, it will just
    /// return the requested path unchanged.
    ///
    /// # Errors
    ///
    /// This method will return an error if the path needs to be expanded but
    /// the expansion fails for some reason (e.g. the requested path does not
    /// exist).
    fn target(&self) -> std::io::Result<std::borrow::Cow<PathBuf>> {
        use std::borrow::Cow::*;

        if self.follow_symlink {
            self.path.canonicalize().map(Owned)
        } else {
            Ok(Borrowed(&self.path))
        }
    }
}

/// A response type for the stat action.
#[derive(Debug)]
pub struct Response {
    /// A path to the file that the result corresponds to.
    path: PathBuf,
    /// Metadata about the file.
    metadata: Metadata,
    /// A path to the pointed file (in case of a symlink).
    symlink: Option<PathBuf>,
    /// Extended attributes of the file.
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
    /// Additional Linux-specific file flags.
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
    // TODO: Add support for collecting file flags on macOS.
}

/// An error type for failures that can occur during the stat action.
#[derive(Debug)]
enum Error {
    /// A failure occurred during the attempt to collect file metadata.
    Metadata(std::io::Error),
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            Metadata(ref error) => Some(error),
        }
    }
}

impl std::fmt::Display for Error {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            Metadata(ref error) => {
                write!(fmt, "unable to collect metadata: {}", error)
            }
        }
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

/// Handles requests for the file stat action.
pub fn handle<S>(session: &mut S, request: Request) -> session::Result<()>
where
    S: Session,
{
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)
    } else {
        std::fs::symlink_metadata(&request.path)
    }.map_err(Error::Metadata)?;

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_family = "unix")]
    let ext_attrs = if request.collect_ext_attrs {
        ext_attrs(&request)
    } else {
        vec!()
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        #[cfg(target_family = "unix")]
        ext_attrs: ext_attrs,
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    session.reply(response)?;

    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
            follow_symlink: proto.follow_symlink.unwrap_or(false),
            collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            #[cfg(target_family = "unix")]
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            #[cfg(target_os = "linux")]
            st_flags_linux: self.flags_linux,
            ..self.metadata.into_lossy()
        }
    }
}

/// Collects extended attributes of a file.
#[cfg(target_family = "unix")]
fn ext_attrs(request: &Request) -> Vec<crate::fs::unix::ExtAttr> {
    use std::borrow::Borrow as _;

    let path = match request.target() {
        Ok(path) => path,
        Err(error) => {
            warn! {
                "failed to expand '{path}': {cause}",
                path = request.path.display(),
                cause = error
            };
            return vec!();
        }
    };

    // TODO: Make the `ack!` macro more expressive and then simplify it.
    match crate::fs::unix::ext_attrs::<PathBuf>(path.borrow()) {
        Ok(ext_attrs) => ext_attrs.collect(),
        Err(error) => {
            warn! {
                "failed to collect attributes for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            };
            vec!()
        },
    }
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    #[test]
    fn test_handle_with_non_existent_file() {
        let tempdir = tempfile::tempdir().unwrap();

        let request = Request {
            path: tempdir.path().join("foo").to_path_buf(),
            follow_symlink: false,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_err());
    }

    #[test]
    fn test_handle_with_regular_file() {
        let tempdir = tempfile::tempdir().unwrap();
        File::create(tempdir.path().join("foo")).unwrap();

        let request = Request {
            path: tempdir.path().join("foo").to_path_buf(),
            follow_symlink: false,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, tempdir.path().join("foo"));
        assert!(reply.metadata.is_file());
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_handle_with_link() {
        let tempdir = tempfile::tempdir().unwrap();
        let symlink = tempdir.path().join("foo");
        let target = tempdir.path().join("bar");

        File::create(&target).unwrap();
        std::os::unix::fs::symlink(&target, &symlink).unwrap();

        let request = Request {
            path: symlink.clone(),
            follow_symlink: false,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, symlink);
        assert_eq!(reply.symlink, Some(target));
        assert!(reply.metadata.file_type().is_symlink());
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_handle_with_two_links() {
        use std::os::unix::fs::symlink;

        let tempdir = tempfile::tempdir().unwrap();
        let symlink_to_symlink = tempdir.path().join("foo");
        let symlink_to_target = tempdir.path().join("bar");
        let target = tempdir.path().join("baz");

        File::create(&target).unwrap();
        symlink(&target, &symlink_to_target).unwrap();
        symlink(&symlink_to_target, &symlink_to_symlink).unwrap();

        let request = Request {
            path: symlink_to_symlink.clone(),
            follow_symlink: false,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, symlink_to_symlink);
        assert_eq!(reply.symlink, Some(symlink_to_target));
        assert!(reply.metadata.file_type().is_symlink());
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_handle_with_link_and_follow_symlink() {
        let tempdir = tempfile::tempdir().unwrap();
        let symlink = tempdir.path().join("foo");
        let target = tempdir.path().join("bar");

        File::create(&target).unwrap();
        std::os::unix::fs::symlink(&target, &symlink).unwrap();

        let request = Request {
            path: symlink.clone(),
            follow_symlink: true,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, symlink);
        assert_eq!(reply.symlink, None);
        assert!(reply.metadata.is_file());
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_handle_with_two_links_and_follow_symlink() {
        use std::os::unix::fs::symlink;

        let tempdir = tempfile::tempdir().unwrap();
        let symlink_to_symlink = tempdir.path().join("foo");
        let symlink_to_target = tempdir.path().join("bar");
        let target = tempdir.path().join("baz");

        File::create(&target).unwrap();
        symlink(&target, &symlink_to_target).unwrap();
        symlink(&symlink_to_target, &symlink_to_symlink).unwrap();

        let request = Request {
            path: symlink_to_symlink.clone(),
            follow_symlink: true,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, symlink_to_symlink);
        assert_eq!(reply.symlink, None);
        assert!(reply.metadata.is_file());
    }

    // TODO: Write tests for collection of extra file flags on Linux.

    #[cfg(all(target_os = "linux", feature = "test-setfattr"))]
    #[test]
    fn test_handle_with_ext_attrs_on_linux() {
        let tempdir = tempfile::tempdir().unwrap();
        let tempfile = tempdir.path().join("foo");
        std::fs::File::create(&tempfile).unwrap();

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg("user.norf")
                .arg("--value").arg("quux")
                .arg(&tempfile)
                .status()
                .unwrap()
                .success()
        };

        let request = Request {
            path: tempfile.clone(),
            follow_symlink: false,
            collect_ext_attrs: true,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.ext_attrs.len(), 1);
        assert_eq!(reply.ext_attrs[0].name, "user.norf");
        assert_eq!(reply.ext_attrs[0].value, Some(b"quux".to_vec()));
    }

    #[cfg(all(target_os = "linux", feature = "test-setfattr"))]
    #[test]
    fn test_handle_with_ext_attrs_and_follow_symlink_on_linux() {
        let tempdir = tempfile::tempdir().unwrap();
        let symlink = tempdir.path().join("foo");
        let target = tempdir.path().join("bar");

        std::fs::File::create(&target).unwrap();
        std::os::unix::fs::symlink(&target, &symlink).unwrap();

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg("user.norf")
                .arg("--value").arg("quux")
                .arg(&target)
                .status()
                .unwrap()
                .success()
        };

        let request = Request {
            path: symlink.clone(),
            follow_symlink: true,
            collect_ext_attrs: true,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.ext_attrs.len(), 1);
        assert_eq!(reply.ext_attrs[0].name, "user.norf");
        assert_eq!(reply.ext_attrs[0].value, Some(b"quux".to_vec()));
    }
}
<commit_msg>Improve the documentation.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the file stat action.
//!
//! A file stat action collects filesystem metadata associated with a particular
//! file.
//!
//! Note that the gathered bits of information differ across platforms, e.g. on
//! Linux there is a notion of symlinks whereas on Windows no such thing exists.
//! Therefore, on Linux the results might include additional information about
//! the symlink (like the file it points to).

use std::fs::Metadata;
use std::path::PathBuf;

use log::warn;

use crate::session::{self, Session};

/// A request type for the stat action.
#[derive(Debug)]
pub struct Request {
    /// A path to the file to stat.
    path: PathBuf,
    /// Whether to collect extended file attributes.
    collect_ext_attrs: bool,
    /// Whether, in case of a symlink, to collect data about the linked file.
    follow_symlink: bool,
}

impl Request {

    /// Obtains a (potentially expanded) path that this request corresponds to.
    ///
    /// In case of requests that wish to follow symlinks, it will return a path
    /// to the symlink target (in case there is such). Otherwise, it will just
    /// return the requested path unchanged.
    ///
    /// # Errors
    ///
    /// This method will return an error if the path needs to be expanded but
    /// the expansion fails for some reason (e.g. the requested path does not
    /// exist).
    fn target(&self) -> std::io::Result<std::borrow::Cow<PathBuf>> {
        use std::borrow::Cow::*;

        if self.follow_symlink {
            self.path.canonicalize().map(Owned)
        } else {
            Ok(Borrowed(&self.path))
        }
    }
}

/// A response type for the stat action.
#[derive(Debug)]
pub struct Response {
    /// A path to the file that the result corresponds to.
    path: PathBuf,
    /// Metadata about the file.
    metadata: Metadata,
    /// A path to the pointed file (in case of a symlink).
    symlink: Option<PathBuf>,
    /// Extended attributes of the file.
    #[cfg(target_family = "unix")]
    ext_attrs: Vec<crate::fs::unix::ExtAttr>,
    /// Additional Linux-specific file flags.
    #[cfg(target_os = "linux")]
    flags_linux: Option<u32>,
    // TODO: Add support for collecting file flags on macOS.
}

/// An error type for failures that can occur during the stat action.
#[derive(Debug)]
enum Error {
    /// A failure occurred during the attempt to collect file metadata.
    Metadata(std::io::Error),
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            Metadata(ref error) => Some(error),
        }
    }
}

impl std::fmt::Display for Error {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            Metadata(ref error) => {
                write!(fmt, "unable to collect metadata: {}", error)
            }
        }
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

/// Handles requests for the file stat action.
pub fn handle<S>(session: &mut S, request: Request) -> session::Result<()>
where
    S: Session,
{
    let metadata = if request.follow_symlink {
        std::fs::metadata(&request.path)
    } else {
        std::fs::symlink_metadata(&request.path)
    }.map_err(Error::Metadata)?;

    let symlink = if metadata.file_type().is_symlink() {
        std::fs::read_link(&request.path).map_err(|error| {
            // TODO: Make the `ack!` macro more expressive and rewrite it.
            warn! {
                "failed to read symlink for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            }
        }).ok()
    } else {
        None
    };

    #[cfg(target_family = "unix")]
    let ext_attrs = if request.collect_ext_attrs {
        ext_attrs(&request)
    } else {
        vec!()
    };

    #[cfg(target_os = "linux")]
    let flags_linux = crate::fs::linux::flags(&request.path).map_err(|error| {
        // TODO: Make the `ack!` macro more expressive and rewrite it.
        warn! {
            "failed to collect flags for '{path}': {cause}",
            path = request.path.display(),
            cause = error,
        }
    }).ok();

    let response = Response {
        path: request.path,
        metadata: metadata,
        symlink: symlink,
        #[cfg(target_family = "unix")]
        ext_attrs: ext_attrs,
        #[cfg(target_os = "linux")]
        flags_linux: flags_linux,
    };

    session.reply(response)?;

    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::GetFileStatRequest;

    fn from_proto(proto: Self::Proto) -> Result<Self, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
            follow_symlink: proto.follow_symlink.unwrap_or(false),
            collect_ext_attrs: proto.collect_ext_attrs.unwrap_or(false),
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            #[cfg(target_family = "unix")]
            ext_attrs: self.ext_attrs.into_iter().map(Into::into).collect(),
            #[cfg(target_os = "linux")]
            st_flags_linux: self.flags_linux,
            ..self.metadata.into_lossy()
        }
    }
}

/// Collects extended attributes of a file specified by the request.
#[cfg(target_family = "unix")]
fn ext_attrs(request: &Request) -> Vec<crate::fs::unix::ExtAttr> {
    use std::borrow::Borrow as _;

    let path = match request.target() {
        Ok(path) => path,
        Err(error) => {
            warn! {
                "failed to expand '{path}': {cause}",
                path = request.path.display(),
                cause = error
            };
            return vec!();
        }
    };

    // TODO: Make the `ack!` macro more expressive and then simplify it.
    match crate::fs::unix::ext_attrs::<PathBuf>(path.borrow()) {
        Ok(ext_attrs) => ext_attrs.collect(),
        Err(error) => {
            warn! {
                "failed to collect attributes for '{path}': {cause}",
                path = request.path.display(),
                cause = error,
            };
            vec!()
        },
    }
}

#[cfg(test)]
mod tests {

    use std::fs::File;

    use super::*;

    #[test]
    fn test_handle_with_non_existent_file() {
        let tempdir = tempfile::tempdir().unwrap();

        let request = Request {
            path: tempdir.path().join("foo").to_path_buf(),
            follow_symlink: false,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_err());
    }

    #[test]
    fn test_handle_with_regular_file() {
        let tempdir = tempfile::tempdir().unwrap();
        File::create(tempdir.path().join("foo")).unwrap();

        let request = Request {
            path: tempdir.path().join("foo").to_path_buf(),
            follow_symlink: false,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, tempdir.path().join("foo"));
        assert!(reply.metadata.is_file());
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_handle_with_link() {
        let tempdir = tempfile::tempdir().unwrap();
        let symlink = tempdir.path().join("foo");
        let target = tempdir.path().join("bar");

        File::create(&target).unwrap();
        std::os::unix::fs::symlink(&target, &symlink).unwrap();

        let request = Request {
            path: symlink.clone(),
            follow_symlink: false,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, symlink);
        assert_eq!(reply.symlink, Some(target));
        assert!(reply.metadata.file_type().is_symlink());
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_handle_with_two_links() {
        use std::os::unix::fs::symlink;

        let tempdir = tempfile::tempdir().unwrap();
        let symlink_to_symlink = tempdir.path().join("foo");
        let symlink_to_target = tempdir.path().join("bar");
        let target = tempdir.path().join("baz");

        File::create(&target).unwrap();
        symlink(&target, &symlink_to_target).unwrap();
        symlink(&symlink_to_target, &symlink_to_symlink).unwrap();

        let request = Request {
            path: symlink_to_symlink.clone(),
            follow_symlink: false,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, symlink_to_symlink);
        assert_eq!(reply.symlink, Some(symlink_to_target));
        assert!(reply.metadata.file_type().is_symlink());
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_handle_with_link_and_follow_symlink() {
        let tempdir = tempfile::tempdir().unwrap();
        let symlink = tempdir.path().join("foo");
        let target = tempdir.path().join("bar");

        File::create(&target).unwrap();
        std::os::unix::fs::symlink(&target, &symlink).unwrap();

        let request = Request {
            path: symlink.clone(),
            follow_symlink: true,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, symlink);
        assert_eq!(reply.symlink, None);
        assert!(reply.metadata.is_file());
    }

    // Symlinking is supported only on Unix-like systems.
    #[cfg(target_family = "unix")]
    #[test]
    fn test_handle_with_two_links_and_follow_symlink() {
        use std::os::unix::fs::symlink;

        let tempdir = tempfile::tempdir().unwrap();
        let symlink_to_symlink = tempdir.path().join("foo");
        let symlink_to_target = tempdir.path().join("bar");
        let target = tempdir.path().join("baz");

        File::create(&target).unwrap();
        symlink(&target, &symlink_to_target).unwrap();
        symlink(&symlink_to_target, &symlink_to_symlink).unwrap();

        let request = Request {
            path: symlink_to_symlink.clone(),
            follow_symlink: true,
            collect_ext_attrs: false,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.path, symlink_to_symlink);
        assert_eq!(reply.symlink, None);
        assert!(reply.metadata.is_file());
    }

    // TODO: Write tests for collection of extra file flags on Linux.

    #[cfg(all(target_os = "linux", feature = "test-setfattr"))]
    #[test]
    fn test_handle_with_ext_attrs_on_linux() {
        let tempdir = tempfile::tempdir().unwrap();
        let tempfile = tempdir.path().join("foo");
        std::fs::File::create(&tempfile).unwrap();

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg("user.norf")
                .arg("--value").arg("quux")
                .arg(&tempfile)
                .status()
                .unwrap()
                .success()
        };

        let request = Request {
            path: tempfile.clone(),
            follow_symlink: false,
            collect_ext_attrs: true,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.ext_attrs.len(), 1);
        assert_eq!(reply.ext_attrs[0].name, "user.norf");
        assert_eq!(reply.ext_attrs[0].value, Some(b"quux".to_vec()));
    }

    #[cfg(all(target_os = "linux", feature = "test-setfattr"))]
    #[test]
    fn test_handle_with_ext_attrs_and_follow_symlink_on_linux() {
        let tempdir = tempfile::tempdir().unwrap();
        let symlink = tempdir.path().join("foo");
        let target = tempdir.path().join("bar");

        std::fs::File::create(&target).unwrap();
        std::os::unix::fs::symlink(&target, &symlink).unwrap();

        assert! {
            std::process::Command::new("setfattr")
                .arg("--name").arg("user.norf")
                .arg("--value").arg("quux")
                .arg(&target)
                .status()
                .unwrap()
                .success()
        };

        let request = Request {
            path: symlink.clone(),
            follow_symlink: true,
            collect_ext_attrs: true,
        };

        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        assert_eq!(session.reply_count(), 1);

        let reply = session.reply::<Response>(0);
        assert_eq!(reply.ext_attrs.len(), 1);
        assert_eq!(reply.ext_attrs[0].name, "user.norf");
        assert_eq!(reply.ext_attrs[0].value, Some(b"quux".to_vec()));
    }
}
<|endoftext|>