<commit_before>//! Tools for working with video files.

use failure::ResultExt;
use num::rational::Ratio;
use regex::Regex;
use serde::{Deserialize, Deserializer};
use serde::de;
use serde_json;
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::result;
use std::str::{FromStr, from_utf8};

use errors::*;
use lang::Lang;
use time::Period;

/// Information about an MP3 track (optional).
#[derive(Debug, Default)]
#[allow(missing_docs)]
pub struct Id3Metadata {
    pub genre: Option<String>,
    pub artist: Option<String>,
    pub album: Option<String>,
    pub track_number: Option<(usize, usize)>,
    pub track_name: Option<String>,
    pub lyrics: Option<String>,
}

impl Id3Metadata {
    fn add_args(&self, cmd: &mut Command) {
        if let Some(ref genre) = self.genre {
            cmd.arg("-metadata").arg(format!("genre={}", genre));
        }
        if let Some(ref artist) = self.artist {
            cmd.arg("-metadata").arg(format!("artist={}", artist));
        }
        if let Some(ref album) = self.album {
            cmd.arg("-metadata").arg(format!("album={}", album));
        }
        if let Some((track, total)) = self.track_number {
            cmd.arg("-metadata")
                .arg(format!("track={}/{}", track, total));
        }
        if let Some(ref track_name) = self.track_name {
            cmd.arg("-metadata").arg(format!("title={}", track_name));
        }
        if let Some(ref lyrics) = self.lyrics {
            cmd.arg("-metadata").arg(format!("lyrics={}", lyrics));
        }
    }
}

/// Individual streams inside a video are labelled with a codec type.
#[derive(Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum CodecType {
    Audio,
    Video,
    Subtitle,
    Other(String),
}

impl<'de> Deserialize<'de> for CodecType {
    fn deserialize<D: Deserializer<'de>>(d: D) -> result::Result<Self, D::Error> {
        let s = String::deserialize(d)?;
        match &s[..] {
            "audio" => Ok(CodecType::Audio),
            "video" => Ok(CodecType::Video),
            "subtitle" => Ok(CodecType::Subtitle),
            s => Ok(CodecType::Other(s.to_owned())),
        }
    }
}

/// A wrapper around `Ratio` with custom serialization support.
#[derive(Debug)]
pub struct Fraction(Ratio<u32>);

impl Fraction {
    fn deserialize_parts<'de, D>(d: D) -> result::Result<(u32, u32), D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(d)?;
        let re = Regex::new(r"^(\d+)/(\d+)$").unwrap();
        let cap = re.captures(&s).ok_or_else(|| {
            <D::Error as de::Error>::custom(format!("Expected fraction: {}", &s))
        })?;
        Ok((
            FromStr::from_str(cap.get(1).unwrap().as_str()).unwrap(),
            FromStr::from_str(cap.get(2).unwrap().as_str()).unwrap(),
        ))
    }
}

impl<'de> Deserialize<'de> for Fraction {
    fn deserialize<D: Deserializer<'de>>(d: D) -> result::Result<Self, D::Error> {
        let (num, denom) = Fraction::deserialize_parts(d)?;
        if denom == 0 {
            Err(<D::Error as de::Error>::custom(
                "Found fraction with a denominator of 0",
            ))
        } else {
            Ok(Fraction(Ratio::new(num, denom)))
        }
    }
}

/// An individual content stream within a video.
#[derive(Debug, Deserialize)]
#[allow(missing_docs)]
pub struct Stream {
    pub index: usize,
    pub codec_type: CodecType,
    tags: Option<BTreeMap<String, String>>,
}

impl Stream {
    /// Return the language associated with this stream, if we can figure
    /// it out.
    pub fn language(&self) -> Option<Lang> {
        self.tags
            .as_ref()
            .and_then(|tags| tags.get("language"))
            .and_then(|lang| Lang::iso639(lang).ok())
    }
}

#[test]
fn test_stream_decode() {
    let json = "
{
  \"index\" : 2,
  \"codec_name\" : \"aac\",
  \"codec_long_name\" : \"AAC (Advanced Audio Coding)\",
  \"codec_type\" : \"audio\",
  \"codec_time_base\" : \"1/48000\",
  \"codec_tag_string\" : \"[0][0][0][0]\",
  \"codec_tag\" : \"0x0000\",
  \"sample_rate\" : \"48000.000000\",
  \"channels\" : 2,
  \"bits_per_sample\" : 0,
  \"avg_frame_rate\" : \"0/0\",
  \"time_base\" : \"1/1000\",
  \"start_time\" : \"0.000000\",
  \"duration\" : \"N/A\",
  \"tags\" : {
    \"language\" : \"eng\"
  }
}
";
    let stream: Stream = serde_json::from_str(json).unwrap();
    assert_eq!(CodecType::Audio, stream.codec_type);
    assert_eq!(Some(Lang::iso639("en").unwrap()), stream.language())
}

/// What kind of data do we want to extract, and from what position in the
/// video clip?
pub enum ExtractionSpec {
    /// Extract an image at the specified time.
    Image(f32),
    /// Extract an audio clip covering the specified stream and period.
    Audio(Option<usize>, Period, Id3Metadata),
}

impl ExtractionSpec {
    /// The earliest time at which we might need to extract data.
    fn earliest_time(&self) -> f32 {
        match self {
            &ExtractionSpec::Image(time) => time,
            &ExtractionSpec::Audio(_, period, _) => period.begin(),
        }
    }

    /// Can we combine this extraction with others in a giant batch
    /// request?
    fn can_be_batched(&self) -> bool {
        match self {
            // Batch processing of images requires decoding the whole
            // video, but we can do a "fast seek" and extract one image
            // extremely quickly.
            &ExtractionSpec::Image(_) => false,
            _ => true,
        }
    }

    /// Figure out what ffmpeg args we would need to extract the requested
    /// data.  Assume that the "fast seek" feature has been used to start
    /// decoding at `time_base`.
    fn add_args(&self, cmd: &mut Command, time_base: f32) {
        match self {
            &ExtractionSpec::Image(time) => {
                let scale_filter =
                    format!("scale=iw*min(1\\,min({}/iw\\,{}/ih)):-1", 240, 160);
                cmd.arg("-ss")
                    .arg(format!("{}", time - time_base))
                    .arg("-vframes")
                    .arg("1")
                    .arg("-filter_complex")
                    .arg(&scale_filter)
                    .arg("-f")
                    .arg("image2");
            }
            &ExtractionSpec::Audio(stream, period, ref metadata) => {
                if let Some(sid) = stream {
                    cmd.arg("-map").arg(format!("0:{}", sid));
                }
                metadata.add_args(cmd);
                cmd.arg("-ss")
                    .arg(format!("{}", period.begin() - time_base))
                    .arg("-t")
                    .arg(format!("{}", period.duration()));
            }
        }
    }
}

/// Information about what kind of data we want to extract.
pub struct Extraction {
    /// The path to extract to.
    pub path: PathBuf,
    /// What kind of data to extract.
    pub spec: ExtractionSpec,
}

impl Extraction {
    /// Add the necessary args to `cmd` to perform this extraction.
    fn add_args(&self, cmd: &mut Command, time_base: f32) {
        self.spec.add_args(cmd, time_base);
        cmd.arg(self.path.clone());
    }
}

/// Metadata associated with a video.
#[derive(Debug, Deserialize)]
struct Metadata {
    streams: Vec<Stream>,
}

/// Represents a video file on disk.
#[derive(Debug)]
pub struct Video {
    path: PathBuf,
    metadata: Metadata,
}

impl Video {
    /// Create a new video file, given a path.
    pub fn new(path: &Path) -> Result<Video> {
        // Ensure we have an actual file name before doing anything else.
        path.file_name().ok_or_else(|| {
            format_err!(
                "Video path does not have a filename: {}",
                path.to_string_lossy()
            )
        })?;

        // Run our probe command.
        let mkerr = || RunCommand::new("ffprobe");
        let cmd = Command::new("ffprobe")
            .arg("-v")
            .arg("quiet")
            .arg("-show_streams")
            .arg("-of")
            .arg("json")
            .arg(path)
            .output();
        let output = cmd.with_context(|_| mkerr())?;
        let stdout = from_utf8(&output.stdout).with_context(|_| mkerr())?;
        debug!("Video metadata: {}", stdout);
        let metadata = serde_json::from_str(stdout).with_context(|_| mkerr())?;

        Ok(Video {
            path: path.to_owned(),
            metadata: metadata,
        })
    }

    /// Get just the file name of this video file.
    pub fn file_name(&self) -> &OsStr {
        self.path.file_name().unwrap()
    }

    /// Get just the file stem of this video file, stripped of any
    /// extensions.
    pub fn file_stem(&self) -> &OsStr {
        self.path.file_stem().unwrap()
    }

    /// List all the tracks in a video file.
    pub fn streams(&self) -> &[Stream] {
        &self.metadata.streams
    }

    /// Choose the best audio for the specified language.
    pub fn audio_for(&self, lang: Lang) -> Option<usize> {
        self.streams().iter().position(|s| {
            s.codec_type == CodecType::Audio && s.language() == Some(lang)
        })
    }

    /// Create an extraction command using the specified `time_base`.  This
    /// allows us to start extractions at any arbitrary point in the video
    /// rapidly.
    fn extract_command(&self, time_base: f32) -> Command {
        let mut cmd = Command::new("ffmpeg");
        cmd.arg("-ss").arg(format!("{}", time_base));
        cmd.arg("-i").arg(&self.path);
        cmd
    }

    /// Perform a single extraction.
    fn extract_one(&self, extraction: &Extraction) -> Result<()> {
        let time_base = extraction.spec.earliest_time();
        let mut cmd = self.extract_command(time_base);
        extraction.add_args(&mut cmd, time_base);
        cmd.output().with_context(|_| RunCommand::new("ffmpg"))?;
        Ok(())
    }

    /// Perform a batch extraction.  We assume that the extractions are
    /// sorted in temporal order.
    fn extract_batch(&self, extractions: &[&Extraction]) -> Result<()> {
        // Bail early if we have nothing to extract
        if extractions.is_empty() {
            return Ok(());
        }
        let time_base = extractions[0].spec.earliest_time();

        // Build and run our batch extraction command.
        let mut cmd = self.extract_command(time_base);
        for e in extractions {
            assert!(e.spec.can_be_batched());
            e.add_args(&mut cmd, time_base);
        }
        cmd.output().with_context(|_| RunCommand::new("ffmpg"))?;
        Ok(())
    }

    /// Perform a list of extractions as efficiently as possible.  We use a
    /// batch interface to avoid making too many passes through the file.
    /// We assume that the extractions are sorted in temporal order.
    pub fn extract(&self, extractions: &[Extraction]) -> Result<()> {
        let mut batch: Vec<&Extraction> = vec![];
        for e in extractions {
            if e.spec.can_be_batched() {
                batch.push(e);
            } else {
                self.extract_one(e)?;
            }
        }

        for chunk in batch.chunks(20) {
            self.extract_batch(chunk)?;
        }
        Ok(())
    }
}
<commit_msg>Give a better error for missing video files<commit_after>//! Tools for working with video files.

use failure::ResultExt;
use num::rational::Ratio;
use regex::Regex;
use serde::{Deserialize, Deserializer};
use serde::de;
use serde_json;
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::result;
use std::str::{FromStr, from_utf8};

use errors::*;
use lang::Lang;
use time::Period;

/// Information about an MP3 track (optional).
#[derive(Debug, Default)]
#[allow(missing_docs)]
pub struct Id3Metadata {
    pub genre: Option<String>,
    pub artist: Option<String>,
    pub album: Option<String>,
    pub track_number: Option<(usize, usize)>,
    pub track_name: Option<String>,
    pub lyrics: Option<String>,
}

impl Id3Metadata {
    fn add_args(&self, cmd: &mut Command) {
        if let Some(ref genre) = self.genre {
            cmd.arg("-metadata").arg(format!("genre={}", genre));
        }
        if let Some(ref artist) = self.artist {
            cmd.arg("-metadata").arg(format!("artist={}", artist));
        }
        if let Some(ref album) = self.album {
            cmd.arg("-metadata").arg(format!("album={}", album));
        }
        if let Some((track, total)) = self.track_number {
            cmd.arg("-metadata")
                .arg(format!("track={}/{}", track, total));
        }
        if let Some(ref track_name) = self.track_name {
            cmd.arg("-metadata").arg(format!("title={}", track_name));
        }
        if let Some(ref lyrics) = self.lyrics {
            cmd.arg("-metadata").arg(format!("lyrics={}", lyrics));
        }
    }
}

/// Individual streams inside a video are labelled with a codec type.
#[derive(Debug, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum CodecType {
    Audio,
    Video,
    Subtitle,
    Other(String),
}

impl<'de> Deserialize<'de> for CodecType {
    fn deserialize<D: Deserializer<'de>>(d: D) -> result::Result<Self, D::Error> {
        let s = String::deserialize(d)?;
        match &s[..] {
            "audio" => Ok(CodecType::Audio),
            "video" => Ok(CodecType::Video),
            "subtitle" => Ok(CodecType::Subtitle),
            s => Ok(CodecType::Other(s.to_owned())),
        }
    }
}

/// A wrapper around `Ratio` with custom serialization support.
#[derive(Debug)]
pub struct Fraction(Ratio<u32>);

impl Fraction {
    fn deserialize_parts<'de, D>(d: D) -> result::Result<(u32, u32), D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(d)?;
        let re = Regex::new(r"^(\d+)/(\d+)$").unwrap();
        let cap = re.captures(&s).ok_or_else(|| {
            <D::Error as de::Error>::custom(format!("Expected fraction: {}", &s))
        })?;
        Ok((
            FromStr::from_str(cap.get(1).unwrap().as_str()).unwrap(),
            FromStr::from_str(cap.get(2).unwrap().as_str()).unwrap(),
        ))
    }
}

impl<'de> Deserialize<'de> for Fraction {
    fn deserialize<D: Deserializer<'de>>(d: D) -> result::Result<Self, D::Error> {
        let (num, denom) = Fraction::deserialize_parts(d)?;
        if denom == 0 {
            Err(<D::Error as de::Error>::custom(
                "Found fraction with a denominator of 0",
            ))
        } else {
            Ok(Fraction(Ratio::new(num, denom)))
        }
    }
}

/// An individual content stream within a video.
#[derive(Debug, Deserialize)]
#[allow(missing_docs)]
pub struct Stream {
    pub index: usize,
    pub codec_type: CodecType,
    tags: Option<BTreeMap<String, String>>,
}

impl Stream {
    /// Return the language associated with this stream, if we can figure
    /// it out.
    pub fn language(&self) -> Option<Lang> {
        self.tags
            .as_ref()
            .and_then(|tags| tags.get("language"))
            .and_then(|lang| Lang::iso639(lang).ok())
    }
}

#[test]
fn test_stream_decode() {
    let json = "
{
  \"index\" : 2,
  \"codec_name\" : \"aac\",
  \"codec_long_name\" : \"AAC (Advanced Audio Coding)\",
  \"codec_type\" : \"audio\",
  \"codec_time_base\" : \"1/48000\",
  \"codec_tag_string\" : \"[0][0][0][0]\",
  \"codec_tag\" : \"0x0000\",
  \"sample_rate\" : \"48000.000000\",
  \"channels\" : 2,
  \"bits_per_sample\" : 0,
  \"avg_frame_rate\" : \"0/0\",
  \"time_base\" : \"1/1000\",
  \"start_time\" : \"0.000000\",
  \"duration\" : \"N/A\",
  \"tags\" : {
    \"language\" : \"eng\"
  }
}
";
    let stream: Stream = serde_json::from_str(json).unwrap();
    assert_eq!(CodecType::Audio, stream.codec_type);
    assert_eq!(Some(Lang::iso639("en").unwrap()), stream.language())
}

/// What kind of data do we want to extract, and from what position in the
/// video clip?
pub enum ExtractionSpec {
    /// Extract an image at the specified time.
    Image(f32),
    /// Extract an audio clip covering the specified stream and period.
    Audio(Option<usize>, Period, Id3Metadata),
}

impl ExtractionSpec {
    /// The earliest time at which we might need to extract data.
    fn earliest_time(&self) -> f32 {
        match self {
            &ExtractionSpec::Image(time) => time,
            &ExtractionSpec::Audio(_, period, _) => period.begin(),
        }
    }

    /// Can we combine this extraction with others in a giant batch
    /// request?
    fn can_be_batched(&self) -> bool {
        match self {
            // Batch processing of images requires decoding the whole
            // video, but we can do a "fast seek" and extract one image
            // extremely quickly.
            &ExtractionSpec::Image(_) => false,
            _ => true,
        }
    }

    /// Figure out what ffmpeg args we would need to extract the requested
    /// data.  Assume that the "fast seek" feature has been used to start
    /// decoding at `time_base`.
    fn add_args(&self, cmd: &mut Command, time_base: f32) {
        match self {
            &ExtractionSpec::Image(time) => {
                let scale_filter =
                    format!("scale=iw*min(1\\,min({}/iw\\,{}/ih)):-1", 240, 160);
                cmd.arg("-ss")
                    .arg(format!("{}", time - time_base))
                    .arg("-vframes")
                    .arg("1")
                    .arg("-filter_complex")
                    .arg(&scale_filter)
                    .arg("-f")
                    .arg("image2");
            }
            &ExtractionSpec::Audio(stream, period, ref metadata) => {
                if let Some(sid) = stream {
                    cmd.arg("-map").arg(format!("0:{}", sid));
                }
                metadata.add_args(cmd);
                cmd.arg("-ss")
                    .arg(format!("{}", period.begin() - time_base))
                    .arg("-t")
                    .arg(format!("{}", period.duration()));
            }
        }
    }
}

/// Information about what kind of data we want to extract.
pub struct Extraction {
    /// The path to extract to.
    pub path: PathBuf,
    /// What kind of data to extract.
    pub spec: ExtractionSpec,
}

impl Extraction {
    /// Add the necessary args to `cmd` to perform this extraction.
    fn add_args(&self, cmd: &mut Command, time_base: f32) {
        self.spec.add_args(cmd, time_base);
        cmd.arg(self.path.clone());
    }
}

/// Metadata associated with a video.
#[derive(Debug, Deserialize)]
struct Metadata {
    streams: Vec<Stream>,
}

/// Represents a video file on disk.
#[derive(Debug)]
pub struct Video {
    path: PathBuf,
    metadata: Metadata,
}

impl Video {
    /// Create a new video file, given a path.
    pub fn new(path: &Path) -> Result<Video> {
        // Ensure we have an actual file before doing anything else.
        if !path.is_file() {
            return Err(format_err!("No such file {:?}", path.display()))
        }

        // Run our probe command.
        let mkerr = || RunCommand::new("ffprobe");
        let cmd = Command::new("ffprobe")
            .arg("-v")
            .arg("quiet")
            .arg("-show_streams")
            .arg("-of")
            .arg("json")
            .arg(path)
            .output();
        let output = cmd.with_context(|_| mkerr())?;
        let stdout = from_utf8(&output.stdout).with_context(|_| mkerr())?;
        debug!("Video metadata: {}", stdout);
        let metadata = serde_json::from_str(stdout).with_context(|_| mkerr())?;

        Ok(Video {
            path: path.to_owned(),
            metadata: metadata,
        })
    }

    /// Get just the file name of this video file.
    pub fn file_name(&self) -> &OsStr {
        self.path.file_name().unwrap()
    }

    /// Get just the file stem of this video file, stripped of any
    /// extensions.
    pub fn file_stem(&self) -> &OsStr {
        self.path.file_stem().unwrap()
    }

    /// List all the tracks in a video file.
    pub fn streams(&self) -> &[Stream] {
        &self.metadata.streams
    }

    /// Choose the best audio for the specified language.
    pub fn audio_for(&self, lang: Lang) -> Option<usize> {
        self.streams().iter().position(|s| {
            s.codec_type == CodecType::Audio && s.language() == Some(lang)
        })
    }

    /// Create an extraction command using the specified `time_base`.  This
    /// allows us to start extractions at any arbitrary point in the video
    /// rapidly.
    fn extract_command(&self, time_base: f32) -> Command {
        let mut cmd = Command::new("ffmpeg");
        cmd.arg("-ss").arg(format!("{}", time_base));
        cmd.arg("-i").arg(&self.path);
        cmd
    }

    /// Perform a single extraction.
    fn extract_one(&self, extraction: &Extraction) -> Result<()> {
        let time_base = extraction.spec.earliest_time();
        let mut cmd = self.extract_command(time_base);
        extraction.add_args(&mut cmd, time_base);
        cmd.output().with_context(|_| RunCommand::new("ffmpg"))?;
        Ok(())
    }

    /// Perform a batch extraction.  We assume that the extractions are
    /// sorted in temporal order.
    fn extract_batch(&self, extractions: &[&Extraction]) -> Result<()> {
        // Bail early if we have nothing to extract
        if extractions.is_empty() {
            return Ok(());
        }
        let time_base = extractions[0].spec.earliest_time();

        // Build and run our batch extraction command.
        let mut cmd = self.extract_command(time_base);
        for e in extractions {
            assert!(e.spec.can_be_batched());
            e.add_args(&mut cmd, time_base);
        }
        cmd.output().with_context(|_| RunCommand::new("ffmpg"))?;
        Ok(())
    }

    /// Perform a list of extractions as efficiently as possible.  We use a
    /// batch interface to avoid making too many passes through the file.
    /// We assume that the extractions are sorted in temporal order.
    pub fn extract(&self, extractions: &[Extraction]) -> Result<()> {
        let mut batch: Vec<&Extraction> = vec![];
        for e in extractions {
            if e.spec.can_be_batched() {
                batch.push(e);
            } else {
                self.extract_one(e)?;
            }
        }

        for chunk in batch.chunks(20) {
            self.extract_batch(chunk)?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (vars) fperm srwx

pub fn parse_numeric(fperm: u32, mut mode: &str) -> Result<u32, String> {
    let (op, pos) = parse_op(mode, Some('='))?;
    mode = mode[pos..].trim_start_matches('0');
    if mode.len() > 4 {
        Err(format!("mode is too large ({} > 7777)", mode))
    } else {
        match u32::from_str_radix(mode, 8) {
            Ok(change) => Ok(match op {
                '+' => fperm | change,
                '-' => fperm & !change,
                '=' => change,
                _ => unreachable!(),
            }),
            Err(err) => Err(err.to_string()),
        }
    }
}

pub fn parse_symbolic(
    mut fperm: u32,
    mut mode: &str,
    considering_dir: bool,
) -> Result<u32, String> {
    #[cfg(unix)]
    use libc::umask;

    #[cfg(target_os = "redox")]
    unsafe fn umask(_mask: u32) -> u32 {
        // XXX Redox does not currently have umask
        0
    }

    let (mask, pos) = parse_levels(mode);
    if pos == mode.len() {
        return Err(format!("invalid mode ({})", mode));
    }
    let respect_umask = pos == 0;
    let last_umask = unsafe { umask(0) };
    mode = &mode[pos..];
    while mode.len() > 0 {
        let (op, pos) = parse_op(mode, None)?;
        mode = &mode[pos..];
        let (mut srwx, pos) = parse_change(mode, fperm, considering_dir);
        if respect_umask {
            srwx &= !(last_umask as u32);
        }
        mode = &mode[pos..];
        match op {
            '+' => fperm |= srwx & mask,
            '-' => fperm &= !(srwx & mask),
            '=' => fperm = (fperm & !mask) | (srwx & mask),
            _ => unreachable!(),
        }
    }
    unsafe {
        umask(last_umask);
    }
    Ok(fperm)
}

fn parse_levels(mode: &str) -> (u32, usize) {
    let mut mask = 0;
    let mut pos = 0;
    for ch in mode.chars() {
        mask |= match ch {
            'u' => 0o7700,
            'g' => 0o7070,
            'o' => 0o7007,
            'a' => 0o7777,
            _ => break,
        };
        pos += 1;
    }
    if pos == 0 {
        mask = 0o7777; // default to 'a'
    }
    (mask, pos)
}

fn parse_op(mode: &str, default: Option<char>) -> Result<(char, usize), String> {
    match mode.chars().next() {
        Some(ch) => match ch {
            '+' | '-' | '=' => Ok((ch, 1)),
            _ => match default {
                Some(ch) => Ok((ch, 0)),
                None => Err(format!(
                    "invalid operator (expected +, -, or =, but found {})",
                    ch
                )),
            },
        },
        None => Err("unexpected end of mode".to_owned()),
    }
}

fn parse_change(mode: &str, fperm: u32, considering_dir: bool) -> (u32, usize) {
    let mut srwx = fperm & 0o7000;
    let mut pos = 0;
    for ch in mode.chars() {
        match ch {
            'r' => srwx |= 0o444,
            'w' => srwx |= 0o222,
            'x' => srwx |= 0o111,
            'X' => {
                if considering_dir || (fperm & 0o0111) != 0 {
                    srwx |= 0o111
                }
            }
            's' => srwx |= 0o4000 | 0o2000,
            't' => srwx |= 0o1000,
            'u' => srwx = (fperm & 0o700) | ((fperm >> 3) & 0o070) | ((fperm >> 6) & 0o007),
            'g' => srwx = ((fperm << 3) & 0o700) | (fperm & 0o070) | ((fperm >> 3) & 0o007),
            'o' => srwx = ((fperm << 6) & 0o700) | ((fperm << 3) & 0o070) | (fperm & 0o007),
            _ => break,
        };
        pos += 1;
    }
    if pos == 0 {
        srwx = 0;
    }
    (srwx, pos)
}
<commit_msg>uucore/refactor ~ fix `cargo clippy` complaints (len_zero)<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.

// spell-checker:ignore (vars) fperm srwx

pub fn parse_numeric(fperm: u32, mut mode: &str) -> Result<u32, String> {
    let (op, pos) = parse_op(mode, Some('='))?;
    mode = mode[pos..].trim_start_matches('0');
    if mode.len() > 4 {
        Err(format!("mode is too large ({} > 7777)", mode))
    } else {
        match u32::from_str_radix(mode, 8) {
            Ok(change) => Ok(match op {
                '+' => fperm | change,
                '-' => fperm & !change,
                '=' => change,
                _ => unreachable!(),
            }),
            Err(err) => Err(err.to_string()),
        }
    }
}

pub fn parse_symbolic(
    mut fperm: u32,
    mut mode: &str,
    considering_dir: bool,
) -> Result<u32, String> {
    #[cfg(unix)]
    use libc::umask;

    #[cfg(target_os = "redox")]
    unsafe fn umask(_mask: u32) -> u32 {
        // XXX Redox does not currently have umask
        0
    }

    let (mask, pos) = parse_levels(mode);
    if pos == mode.len() {
        return Err(format!("invalid mode ({})", mode));
    }
    let respect_umask = pos == 0;
    let last_umask = unsafe { umask(0) };
    mode = &mode[pos..];
    while !mode.is_empty() {
        let (op, pos) = parse_op(mode, None)?;
        mode = &mode[pos..];
        let (mut srwx, pos) = parse_change(mode, fperm, considering_dir);
        if respect_umask {
            srwx &= !(last_umask as u32);
        }
        mode = &mode[pos..];
        match op {
            '+' => fperm |= srwx & mask,
            '-' => fperm &= !(srwx & mask),
            '=' => fperm = (fperm & !mask) | (srwx & mask),
            _ => unreachable!(),
        }
    }
    unsafe {
        umask(last_umask);
    }
    Ok(fperm)
}

fn parse_levels(mode: &str) -> (u32, usize) {
    let mut mask = 0;
    let mut pos = 0;
    for ch in mode.chars() {
        mask |= match ch {
            'u' => 0o7700,
            'g' => 0o7070,
            'o' => 0o7007,
            'a' => 0o7777,
            _ => break,
        };
        pos += 1;
    }
    if pos == 0 {
        mask = 0o7777; // default to 'a'
    }
    (mask, pos)
}

fn parse_op(mode: &str, default: Option<char>) -> Result<(char, usize), String> {
    match mode.chars().next() {
        Some(ch) => match ch {
            '+' | '-' | '=' => Ok((ch, 1)),
            _ => match default {
                Some(ch) => Ok((ch, 0)),
                None => Err(format!(
                    "invalid operator (expected +, -, or =, but found {})",
                    ch
                )),
            },
        },
        None => Err("unexpected end of mode".to_owned()),
    }
}

fn parse_change(mode: &str, fperm: u32, considering_dir: bool) -> (u32, usize) {
    let mut srwx = fperm & 0o7000;
    let mut pos = 0;
    for ch in mode.chars() {
        match ch {
            'r' => srwx |= 0o444,
            'w' => srwx |= 0o222,
            'x' => srwx |= 0o111,
            'X' => {
                if considering_dir || (fperm & 0o0111) != 0 {
                    srwx |= 0o111
                }
            }
            's' => srwx |= 0o4000 | 0o2000,
            't' => srwx |= 0o1000,
            'u' => srwx = (fperm & 0o700) | ((fperm >> 3) & 0o070) | ((fperm >> 6) & 0o007),
            'g' => srwx = ((fperm << 3) & 0o700) | (fperm & 0o070) | ((fperm >> 3) & 0o007),
            'o' => srwx = ((fperm << 6) & 0o700) | ((fperm << 3) & 0o070) | (fperm & 0o007),
            _ => break,
        };
        pos += 1;
    }
    if pos == 0 {
        srwx = 0;
    }
    (srwx, pos)
}
<|endoftext|>


<commit_before>#![deny(rust_2018_idioms)]
#![deny(proc_macro_derive_resolution_fallback)]

use irc::client::{prelude::*, ClientStream};

use rendezvous_common::{
    anyhow,
    futures::prelude::*,
    proto::{
        bouncer_service_client::BouncerServiceClient, event, ClientType, Event, Header,
        MessageCreated,
    },
    // ipc,
    tokio,
    tonic::{transport::Channel, Response, Streaming},
    tracing::{self, info, instrument},
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing::init()?;

    let mut irc_client = Client::new("config.toml").await?;
    irc_client.identify()?;
    info!("connected");

    let mut client = BouncerServiceClient::connect("http://[::1]:49252").await?;

    let resp = client
        .subscribe(Header {
            client_type: ClientType::Irc.into(),
        })
        .await?;

    tokio::try_join!(
        handle_irc_stream(irc_client.stream()?, client),
        handle_rpc_stream(resp, irc_client.sender()),
    )?;
    Ok(())
}

#[instrument]
async fn handle_irc_stream(
    mut irc_stream: ClientStream,
    mut client: BouncerServiceClient<Channel>,
) -> anyhow::Result<()> {
    while let Some(irc_msg) = irc_stream.try_next().await? {
        let nickname = irc_msg.source_nickname().unwrap_or("").into();
        match irc_msg.command {
            Command::PRIVMSG(channel, content) => {
                info!("privmsg");
                client
                    .post(Event {
                        header: Some(Header {
                            client_type: ClientType::Irc.into(),
                        }),
                        body: Some(event::Body::MessageCreated(MessageCreated {
                            nickname,
                            channel,
                            content,
                            origin: "".to_owned(),
                        })),
                    })
                    .await?;
            }
            _ => {}
        }
    }
    Ok(())
}

#[instrument]
async fn handle_rpc_stream(
    mut resp: Response<Streaming<Event>>,
    sender: Sender,
) -> anyhow::Result<()> {
    let stream = resp.get_mut();
    while let Some(e) = stream.try_next().await? {
        match e.body {
            Some(event::Body::MessageCreated(MessageCreated {
                nickname,
                channel,
                content,
                ..
            })) => {
                sender.send_privmsg(&channel, &format!("<{}> {}", nickname, content))?;
            }
            _ => {}
        }
    }
    Ok(())
}
<commit_msg>feat: handle multiline message and codeblock<commit_after>#![deny(rust_2018_idioms)]
#![deny(proc_macro_derive_resolution_fallback)]

use std::borrow::Cow;

use irc::client::{prelude::*, ClientStream};

use rendezvous_common::{
    anyhow,
    futures::prelude::*,
    proto::{
        bouncer_service_client::BouncerServiceClient, event, ClientType, Event, Header,
        MessageCreated,
    },
    // ipc,
    tokio,
    tonic::{transport::Channel, Response, Streaming},
    tracing::{self, info, instrument},
};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing::init()?;

    let mut irc_client = Client::new("config.toml").await?;
    irc_client.identify()?;
    info!("connected");

    let mut client = BouncerServiceClient::connect("http://[::1]:49252").await?;

    let resp = client
        .subscribe(Header {
            client_type: ClientType::Irc.into(),
        })
        .await?;

    tokio::try_join!(
        handle_irc_stream(irc_client.stream()?, client),
        handle_rpc_stream(resp, irc_client.sender()),
    )?;
    Ok(())
}

#[instrument]
async fn handle_irc_stream(
    mut irc_stream: ClientStream,
    mut client: BouncerServiceClient<Channel>,
) -> anyhow::Result<()> {
    while let Some(irc_msg) = irc_stream.try_next().await? {
        let nickname = irc_msg.source_nickname().unwrap_or("").into();
        match irc_msg.command {
            Command::PRIVMSG(channel, content) => {
                info!("privmsg");
                client
                    .post(Event {
                        header: Some(Header {
                            client_type: ClientType::Irc.into(),
                        }),
                        body: Some(event::Body::MessageCreated(MessageCreated {
                            nickname,
                            channel,
                            content,
                            origin: "".to_owned(),
                        })),
                    })
                    .await?;
            }
            _ => {}
        }
    }
    Ok(())
}

#[instrument]
async fn handle_rpc_stream(
    mut resp: Response<Streaming<Event>>,
    sender: Sender,
) -> anyhow::Result<()> {
    let stream = resp.get_mut();
    while let Some(e) = stream.try_next().await? {
        match e.body {
            Some(event::Body::MessageCreated(MessageCreated {
                nickname,
                channel,
                content,
                ..
            })) => {
                let mut is_codeblock = false;
                for line in content.split_terminator('\n') {
                    let message = if is_codeblock {
                        Cow::Borrowed(line)
                    } else {
                        Cow::Owned(format!("<{}> {}", nickname, line))
                    };
                    sender.send_privmsg(&channel, &message)?;
                    if line.contains("```") {
                        is_codeblock = !is_codeblock;
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::cell::Cell;

use drop_bomb::DropBomb;

use crate::{
    event::Event,
    ParseError,
    SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
    TokenSet, TokenSource, T,
};

/// `Parser` struct provides the low-level API for
/// navigating through the stream of tokens and
/// constructing the parse tree. The actual parsing
/// happens in the `grammar` module.
///
/// However, the result of this `Parser` is not a real
/// tree, but rather a flat stream of events of the form
/// "start expression, consume number literal,
/// finish expression". See `Event` docs for more.
pub(crate) struct Parser<'t> {
    token_source: &'t mut dyn TokenSource,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'t> Parser<'t> {
    pub(super) fn new(token_source: &'t mut dyn TokenSource) -> Parser<'t> {
        Parser { token_source, events: Vec::new(), steps: Cell::new(0) }
    }

    pub(crate) fn finish(self) -> Vec<Event> {
        self.events
    }

    /// Returns the kind of the current token.
    /// If parser has already reached the end of input,
    /// the special `EOF` kind is returned.
    pub(crate) fn current(&self) -> SyntaxKind {
        self.nth(0)
    }

    /// Lookahead operation: returns the kind of the next nth
    /// token.
    pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
        assert!(n <= 3);

        let steps = self.steps.get();
        assert!(steps <= 10_000_000, "the parser seems stuck");
        self.steps.set(steps + 1);

        self.token_source.lookahead_nth(n).kind
    }

    /// Checks if the current token is `kind`.
    pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
        self.nth_at(0, kind)
    }

    pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
        match kind {
            T![-=] => self.at_composite2(n, T![-], T![=]),
            T![->] => self.at_composite2(n, T![-], T![>]),
            T![::] => self.at_composite2(n, T![:], T![:]),
            T![!=] => self.at_composite2(n, T![!], T![=]),
            T![..] => self.at_composite2(n, T![.], T![.]),
            T![*=] => self.at_composite2(n, T![*], T![=]),
            T![/=] => self.at_composite2(n, T![/], T![=]),
            T![&&] => self.at_composite2(n, T![&], T![&]),
            T![&=] => self.at_composite2(n, T![&], T![=]),
            T![%=] => self.at_composite2(n, T![%], T![=]),
            T![^=] => self.at_composite2(n, T![^], T![=]),
            T![+=] => self.at_composite2(n, T![+], T![=]),
            T![<<] => self.at_composite2(n, T![<], T![<]),
            T![<=] => self.at_composite2(n, T![<], T![=]),
            T![==] => self.at_composite2(n, T![=], T![=]),
            T![=>] => self.at_composite2(n, T![=], T![>]),
            T![>=] => self.at_composite2(n, T![>], T![=]),
            T![>>] => self.at_composite2(n, T![>], T![>]),
            T![|=] => self.at_composite2(n, T![|], T![=]),
            T![||] => self.at_composite2(n, T![|], T![|]),

            T![...] => self.at_composite3(n, T![.], T![.], T![.]),
            T![..=] => self.at_composite3(n, T![.], T![.], T![=]),
            T![<<=] => self.at_composite3(n, T![<], T![<], T![=]),
            T![>>=] => self.at_composite3(n, T![>], T![>], T![=]),

            _ => self.token_source.lookahead_nth(n).kind == kind,
        }
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
        if !self.at(kind) {
            return false;
        }
        let n_raw_tokens = match kind {
            T![-=]
            | T![->]
            | T![::]
            | T![!=]
            | T![..]
            | T![*=]
            | T![/=]
            | T![&&]
            | T![&=]
            | T![%=]
            | T![^=]
            | T![+=]
            | T![<<]
            | T![<=]
            | T![==]
            | T![=>]
            | T![>=]
            | T![>>]
            | T![|=]
            | T![||] => 2,

            T![...] | T![..=] | T![<<=] | T![>>=] => 3,
            _ => 1,
        };
        self.do_bump(kind, n_raw_tokens);
        true
    }

    fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
        let t1 = self.token_source.lookahead_nth(n);
        let t2 = self.token_source.lookahead_nth(n + 1);
        t1.kind == k1 && t1.is_jointed_to_next && t2.kind == k2
    }

    fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool {
        let t1 = self.token_source.lookahead_nth(n);
        let t2 = self.token_source.lookahead_nth(n + 1);
        let t3 = self.token_source.lookahead_nth(n + 2);
        (t1.kind == k1 && t1.is_jointed_to_next)
            && (t2.kind == k2 && t2.is_jointed_to_next)
            && t3.kind == k3
    }

    /// Checks if the current token is in `kinds`.
    pub(crate) fn at_ts(&self, kinds: TokenSet) -> bool {
        kinds.contains(self.current())
    }

    /// Checks if the current token is contextual keyword with text `t`.
    pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool {
        self.token_source.is_keyword(kw)
    }

    /// Starts a new node in the syntax tree. All nodes and tokens
    /// consumed between the `start` and the corresponding `Marker::complete`
    /// belong to the same node.
    pub(crate) fn start(&mut self) -> Marker {
        let pos = self.events.len() as u32;
        self.push_event(Event::tombstone());
        Marker::new(pos)
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn bump(&mut self, kind: SyntaxKind) {
        assert!(self.eat(kind));
    }

    /// Advances the parser by one token
    pub(crate) fn bump_any(&mut self) {
        let kind = self.nth(0);
        if kind == EOF {
            return;
        }
        self.do_bump(kind, 1)
    }

    /// Advances the parser by one token, remapping its kind.
    /// This is useful to create contextual keywords from
    /// identifiers. For example, the lexer creates an `union`
    /// *identifier* token, but the parser remaps it to the
    /// `union` keyword, and keyword is what ends up in the
    /// final tree.
    pub(crate) fn bump_remap(&mut self, kind: SyntaxKind) {
        if self.nth(0) == EOF {
            // FIXME: panic!?
            return;
        }
        self.do_bump(kind, 1);
    }

    /// Emit error with the `message`
    /// FIXME: this should be much more fancy and support
    /// structured errors with spans and notes, like rustc
    /// does.
    pub(crate) fn error<T: Into<String>>(&mut self, message: T) {
        let msg = ParseError(Box::new(message.into()));
        self.push_event(Event::Error { msg })
    }

    /// Consume the next token if it is `kind` or emit an error
    /// otherwise.
    pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
        if self.eat(kind) {
            return true;
        }
        self.error(format!("expected {:?}", kind));
        false
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_and_bump(&mut self, message: &str) {
        self.err_recover(message, TokenSet::EMPTY);
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
        match self.current() {
            T!['{'] | T!['}'] => {
                self.error(message);
                return;
            }
            _ => (),
        }

        if self.at_ts(recovery) {
            self.error(message);
            return;
        }

        let m = self.start();
        self.error(message);
        self.bump_any();
        m.complete(self, ERROR);
    }

    fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
        for _ in 0..n_raw_tokens {
            self.token_source.bump();
        }

        self.push_event(Event::Token { kind, n_raw_tokens });
    }

    fn push_event(&mut self, event: Event) {
        self.events.push(event)
    }
}

/// See `Parser::start`.
pub(crate) struct Marker {
    pos: u32,
    bomb: DropBomb,
}

impl Marker {
    fn new(pos: u32) -> Marker {
        Marker { pos, bomb: DropBomb::new("Marker must be either completed or abandoned") }
    }

    /// Finishes the syntax tree node and assigns `kind` to it,
    /// and mark the create a `CompletedMarker` for possible future
    /// operation like `.precede()` to deal with forward_parent.
    pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
        self.bomb.defuse();
        let idx = self.pos as usize;
        match p.events[idx] {
            Event::Start { kind: ref mut slot, .. } => {
                *slot = kind;
            }
            _ => unreachable!(),
        }
        let finish_pos = p.events.len() as u32;
        p.push_event(Event::Finish);
        CompletedMarker::new(self.pos, finish_pos, kind)
    }

    /// Abandons the syntax tree node. All its children
    /// are attached to its parent instead.
    pub(crate) fn abandon(mut self, p: &mut Parser) {
        self.bomb.defuse();
        let idx = self.pos as usize;
        if idx == p.events.len() - 1 {
            match p.events.pop() {
                Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
                _ => unreachable!(),
            }
        }
    }
}

pub(crate) struct CompletedMarker {
    start_pos: u32,
    finish_pos: u32,
    kind: SyntaxKind,
}

impl CompletedMarker {
    fn new(start_pos: u32, finish_pos: u32, kind: SyntaxKind) -> Self {
        CompletedMarker { start_pos, finish_pos, kind }
    }

    /// This method allows to create a new node which starts
    /// *before* the current one. That is, parser could start
    /// node `A`, then complete it, and then after parsing the
    /// whole `A`, decide that it should have started some node
    /// `B` before starting `A`. `precede` allows to do exactly
    /// that. See also docs about `forward_parent` in `Event::Start`.
    ///
    /// Given completed events `[START, FINISH]` and its corresponding
    /// `CompletedMarker(pos: 0, _)`.
    /// Append a new `START` events as `[START, FINISH, NEWSTART]`,
    /// then mark `NEWSTART` as `START`'s parent with saving its relative
    /// distance to `NEWSTART` into forward_parent(=2 in this case);
    pub(crate) fn precede(self, p: &mut Parser) -> Marker {
        let new_pos = p.start();
        let idx = self.start_pos as usize;
        match p.events[idx] {
            Event::Start { ref mut forward_parent, .. } => {
                *forward_parent = Some(new_pos.pos - self.start_pos);
            }
            _ => unreachable!(),
        }
        new_pos
    }

    /// Undo this completion and turns into a `Marker`
    pub(crate) fn undo_completion(self, p: &mut Parser) -> Marker {
        let start_idx = self.start_pos as usize;
        let finish_idx = self.finish_pos as usize;
        match p.events[start_idx] {
            Event::Start { ref mut kind, forward_parent: None } => *kind = TOMBSTONE,
            _ => unreachable!(),
        }
        match p.events[finish_idx] {
            ref mut slot @ Event::Finish => *slot = Event::tombstone(),
            _ => unreachable!(),
        }
        Marker::new(self.start_pos)
    }

    pub(crate) fn kind(&self) -> SyntaxKind {
        self.kind
    }
}
<commit_msg>Micro-optimize lookahead in composite tokens<commit_after>//! FIXME: write short doc here

use std::cell::Cell;

use drop_bomb::DropBomb;

use crate::{
    event::Event,
    ParseError,
    SyntaxKind::{self, EOF, ERROR, TOMBSTONE},
    TokenSet, TokenSource, T,
};

/// `Parser` struct provides the low-level API for
/// navigating through the stream of tokens and
/// constructing the parse tree. The actual parsing
/// happens in the `grammar` module.
///
/// However, the result of this `Parser` is not a real
/// tree, but rather a flat stream of events of the form
/// "start expression, consume number literal,
/// finish expression". See `Event` docs for more.
pub(crate) struct Parser<'t> {
    token_source: &'t mut dyn TokenSource,
    events: Vec<Event>,
    steps: Cell<u32>,
}

impl<'t> Parser<'t> {
    pub(super) fn new(token_source: &'t mut dyn TokenSource) -> Parser<'t> {
        Parser { token_source, events: Vec::new(), steps: Cell::new(0) }
    }

    pub(crate) fn finish(self) -> Vec<Event> {
        self.events
    }

    /// Returns the kind of the current token.
    /// If parser has already reached the end of input,
    /// the special `EOF` kind is returned.
    pub(crate) fn current(&self) -> SyntaxKind {
        self.nth(0)
    }

    /// Lookahead operation: returns the kind of the next nth
    /// token.
    pub(crate) fn nth(&self, n: usize) -> SyntaxKind {
        assert!(n <= 3);

        let steps = self.steps.get();
        assert!(steps <= 10_000_000, "the parser seems stuck");
        self.steps.set(steps + 1);

        self.token_source.lookahead_nth(n).kind
    }

    /// Checks if the current token is `kind`.
    pub(crate) fn at(&self, kind: SyntaxKind) -> bool {
        self.nth_at(0, kind)
    }

    pub(crate) fn nth_at(&self, n: usize, kind: SyntaxKind) -> bool {
        match kind {
            T![-=] => self.at_composite2(n, T![-], T![=]),
            T![->] => self.at_composite2(n, T![-], T![>]),
            T![::] => self.at_composite2(n, T![:], T![:]),
            T![!=] => self.at_composite2(n, T![!], T![=]),
            T![..] => self.at_composite2(n, T![.], T![.]),
            T![*=] => self.at_composite2(n, T![*], T![=]),
            T![/=] => self.at_composite2(n, T![/], T![=]),
            T![&&] => self.at_composite2(n, T![&], T![&]),
            T![&=] => self.at_composite2(n, T![&], T![=]),
            T![%=] => self.at_composite2(n, T![%], T![=]),
            T![^=] => self.at_composite2(n, T![^], T![=]),
            T![+=] => self.at_composite2(n, T![+], T![=]),
            T![<<] => self.at_composite2(n, T![<], T![<]),
            T![<=] => self.at_composite2(n, T![<], T![=]),
            T![==] => self.at_composite2(n, T![=], T![=]),
            T![=>] => self.at_composite2(n, T![=], T![>]),
            T![>=] => self.at_composite2(n, T![>], T![=]),
            T![>>] => self.at_composite2(n, T![>], T![>]),
            T![|=] => self.at_composite2(n, T![|], T![=]),
            T![||] => self.at_composite2(n, T![|], T![|]),

            T![...] => self.at_composite3(n, T![.], T![.], T![.]),
            T![..=] => self.at_composite3(n, T![.], T![.], T![=]),
            T![<<=] => self.at_composite3(n, T![<], T![<], T![=]),
            T![>>=] => self.at_composite3(n, T![>], T![>], T![=]),

            _ => self.token_source.lookahead_nth(n).kind == kind,
        }
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn eat(&mut self, kind: SyntaxKind) -> bool {
        if !self.at(kind) {
            return false;
        }
        let n_raw_tokens = match kind {
            T![-=]
            | T![->]
            | T![::]
            | T![!=]
            | T![..]
            | T![*=]
            | T![/=]
            | T![&&]
            | T![&=]
            | T![%=]
            | T![^=]
            | T![+=]
            | T![<<]
            | T![<=]
            | T![==]
            | T![=>]
            | T![>=]
            | T![>>]
            | T![|=]
            | T![||] => 2,

            T![...] | T![..=] | T![<<=] | T![>>=] => 3,
            _ => 1,
        };
        self.do_bump(kind, n_raw_tokens);
        true
    }

    fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
        let t1 = self.token_source.lookahead_nth(n);
        if t1.kind != k1 || !t1.is_jointed_to_next {
            return false;
        }
        let t2 = self.token_source.lookahead_nth(n + 1);
        t2.kind == k2
    }

    fn at_composite3(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind, k3: SyntaxKind) -> bool {
        let t1 = self.token_source.lookahead_nth(n);
        if t1.kind != k1 || !t1.is_jointed_to_next {
            return false;
        }
        let t2 = self.token_source.lookahead_nth(n + 1);
        if t2.kind != k2 || !t2.is_jointed_to_next {
            return false;
        }
        let t3 = self.token_source.lookahead_nth(n + 2);
        t3.kind == k3
    }

    /// Checks if the current token is in `kinds`.
    pub(crate) fn at_ts(&self, kinds: TokenSet) -> bool {
        kinds.contains(self.current())
    }

    /// Checks if the current token is contextual keyword with text `t`.
    pub(crate) fn at_contextual_kw(&self, kw: &str) -> bool {
        self.token_source.is_keyword(kw)
    }

    /// Starts a new node in the syntax tree. All nodes and tokens
    /// consumed between the `start` and the corresponding `Marker::complete`
    /// belong to the same node.
    pub(crate) fn start(&mut self) -> Marker {
        let pos = self.events.len() as u32;
        self.push_event(Event::tombstone());
        Marker::new(pos)
    }

    /// Consume the next token if `kind` matches.
    pub(crate) fn bump(&mut self, kind: SyntaxKind) {
        assert!(self.eat(kind));
    }

    /// Advances the parser by one token
    pub(crate) fn bump_any(&mut self) {
        let kind = self.nth(0);
        if kind == EOF {
            return;
        }
        self.do_bump(kind, 1)
    }

    /// Advances the parser by one token, remapping its kind.
    /// This is useful to create contextual keywords from
    /// identifiers. For example, the lexer creates an `union`
    /// *identifier* token, but the parser remaps it to the
    /// `union` keyword, and keyword is what ends up in the
    /// final tree.
    pub(crate) fn bump_remap(&mut self, kind: SyntaxKind) {
        if self.nth(0) == EOF {
            // FIXME: panic!?
            return;
        }
        self.do_bump(kind, 1);
    }

    /// Emit error with the `message`
    /// FIXME: this should be much more fancy and support
    /// structured errors with spans and notes, like rustc
    /// does.
    pub(crate) fn error<T: Into<String>>(&mut self, message: T) {
        let msg = ParseError(Box::new(message.into()));
        self.push_event(Event::Error { msg })
    }

    /// Consume the next token if it is `kind` or emit an error
    /// otherwise.
    pub(crate) fn expect(&mut self, kind: SyntaxKind) -> bool {
        if self.eat(kind) {
            return true;
        }
        self.error(format!("expected {:?}", kind));
        false
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_and_bump(&mut self, message: &str) {
        self.err_recover(message, TokenSet::EMPTY);
    }

    /// Create an error node and consume the next token.
    pub(crate) fn err_recover(&mut self, message: &str, recovery: TokenSet) {
        match self.current() {
            T!['{'] | T!['}'] => {
                self.error(message);
                return;
            }
            _ => (),
        }

        if self.at_ts(recovery) {
            self.error(message);
            return;
        }

        let m = self.start();
        self.error(message);
        self.bump_any();
        m.complete(self, ERROR);
    }

    fn do_bump(&mut self, kind: SyntaxKind, n_raw_tokens: u8) {
        for _ in 0..n_raw_tokens {
            self.token_source.bump();
        }

        self.push_event(Event::Token { kind, n_raw_tokens });
    }

    fn push_event(&mut self, event: Event) {
        self.events.push(event)
    }
}

/// See `Parser::start`.
pub(crate) struct Marker {
    pos: u32,
    bomb: DropBomb,
}

impl Marker {
    fn new(pos: u32) -> Marker {
        Marker { pos, bomb: DropBomb::new("Marker must be either completed or abandoned") }
    }

    /// Finishes the syntax tree node and assigns `kind` to it,
    /// and mark the create a `CompletedMarker` for possible future
    /// operation like `.precede()` to deal with forward_parent.
    pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker {
        self.bomb.defuse();
        let idx = self.pos as usize;
        match p.events[idx] {
            Event::Start { kind: ref mut slot, .. } => {
                *slot = kind;
            }
            _ => unreachable!(),
        }
        let finish_pos = p.events.len() as u32;
        p.push_event(Event::Finish);
        CompletedMarker::new(self.pos, finish_pos, kind)
    }

    /// Abandons the syntax tree node. All its children
    /// are attached to its parent instead.
    pub(crate) fn abandon(mut self, p: &mut Parser) {
        self.bomb.defuse();
        let idx = self.pos as usize;
        if idx == p.events.len() - 1 {
            match p.events.pop() {
                Some(Event::Start { kind: TOMBSTONE, forward_parent: None }) => (),
                _ => unreachable!(),
            }
        }
    }
}

pub(crate) struct CompletedMarker {
    start_pos: u32,
    finish_pos: u32,
    kind: SyntaxKind,
}

impl CompletedMarker {
    fn new(start_pos: u32, finish_pos: u32, kind: SyntaxKind) -> Self {
        CompletedMarker { start_pos, finish_pos, kind }
    }

    /// This method allows to create a new node which starts
    /// *before* the current one. That is, parser could start
    /// node `A`, then complete it, and then after parsing the
    /// whole `A`, decide that it should have started some node
    /// `B` before starting `A`. `precede` allows to do exactly
    /// that. See also docs about `forward_parent` in `Event::Start`.
    ///
    /// Given completed events `[START, FINISH]` and its corresponding
    /// `CompletedMarker(pos: 0, _)`.
    /// Append a new `START` events as `[START, FINISH, NEWSTART]`,
    /// then mark `NEWSTART` as `START`'s parent with saving its relative
    /// distance to `NEWSTART` into forward_parent(=2 in this case);
    pub(crate) fn precede(self, p: &mut Parser) -> Marker {
        let new_pos = p.start();
        let idx = self.start_pos as usize;
        match p.events[idx] {
            Event::Start { ref mut forward_parent, .. } => {
                *forward_parent = Some(new_pos.pos - self.start_pos);
            }
            _ => unreachable!(),
        }
        new_pos
    }

    /// Undo this completion and turns into a `Marker`
    pub(crate) fn undo_completion(self, p: &mut Parser) -> Marker {
        let start_idx = self.start_pos as usize;
        let finish_idx = self.finish_pos as usize;
        match p.events[start_idx] {
            Event::Start { ref mut kind, forward_parent: None } => *kind = TOMBSTONE,
            _ => unreachable!(),
        }
        match p.events[finish_idx] {
            ref mut slot @ Event::Finish => *slot = Event::tombstone(),
            _ => unreachable!(),
        }
        Marker::new(self.start_pos)
    }

    pub(crate) fn kind(&self) -> SyntaxKind {
        self.kind
    }
}
<|endoftext|>


<commit_before>extern crate clerk;
extern crate itertools;

use std::cell::RefCell;
use std::collections::VecDeque;
use itertools::{multizip, Itertools};

use clerk::{DefaultLines, Delay, Direction, Display, DisplayControlBuilder, DisplayHardwareLayer,
            DisplayPins, EntryModeBuilder, FunctionSetBuilder, Level, SeekFrom, ShiftTo};

pub struct PinMock {
    levels: RefCell<Vec<Level>>,
    directions: RefCell<Vec<Direction>>,
    values: Option<RefCell<VecDeque<u8>>>
}

impl Default for PinMock {
    fn default() -> Self {
        PinMock {
            values: Some(RefCell::new(VecDeque::new())),
            levels: RefCell::new(vec![]),
            directions: RefCell::new(vec![]),
        }
    }
}

impl PinMock {
    pub fn set_value(&mut self, value: u8) {
        match self.values {
            Some(ref mut v) => v.borrow_mut().push_back(value),
            None => ()
        }
    }
}

impl DisplayHardwareLayer for PinMock {
    fn init(&self) {}

    fn cleanup(&self) {}

    fn set_direction(&self, direction: Direction) {
        let mut directions = self.directions.borrow_mut();

        directions.push(direction);
    }

    fn set_level(&self, level: Level) {
        let mut levels = self.levels.borrow_mut();

        levels.push(level);
    }

    fn get_value(&self) -> u8 {
        match self.values {
            Some(ref v) => v.borrow_mut().pop_front().unwrap(),
            None => panic!("No return value specified for current test.")
        }
    }
}

pub struct CustomDelayMock;

impl Delay for CustomDelayMock {
    fn delay_ns(_: u16) {
        // mhh
    }
}

fn setup_display() -> Display<PinMock, DefaultLines, CustomDelayMock> {
    let pins = DisplayPins {
        register_select: PinMock::default(),
        read: PinMock::default(),
        enable: PinMock::default(),
        data4: PinMock::default(),
        data5: PinMock::default(),
        data6: PinMock::default(),
        data7: PinMock::default(),
    };

    Display::from_pins(pins)
}

#[test]
fn test_init() {
    let lcd = setup_display();

    lcd.init(&FunctionSetBuilder::default());

    let pins = lcd.get_pins();
    {
        // check initialization
        let actual = pins.register_select.directions.borrow();
        assert_eq!(actual[..], [Direction::Out]);

        let actual = pins.read.directions.borrow();
        assert_eq!(actual[..], [Direction::Out]);

        let actual = pins.enable.directions.borrow();
        assert_eq!(actual[..], [Direction::Out]);
    }

    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0x33));
    assert_eq!(outp[1], to_level_slice(0x32));
    assert_eq!(outp[2], to_level_slice(0x20));
    assert_eq!(outp[3], to_level_slice(0x01));
}

#[test]
fn test_set_entry_mode() {
    let lcd = setup_display();

    lcd.set_entry_mode(&EntryModeBuilder::default());

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0000_0110));
}

#[test]
fn test_set_display_control() {
    let lcd = setup_display();

    lcd.set_display_control(&DisplayControlBuilder::default());

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0000_1100));
}

#[test]
fn test_shift_cursor_left() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Left(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_0000));
}

#[test]
fn test_shift_cursor_left_with_zero_offset() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Left(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp.len(), 0);
}

#[test]
fn test_shift_cursor_right() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_0100));
}

#[test]
fn test_shift_cursor_right_multiple() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(2));
    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_0100));
    assert_eq!(outp[1], to_level_slice(0b0001_0100));
}

#[test]
fn test_shift_cursor_right_with_zero_offset() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp.len(), 0);
}

#[test]
fn test_shift_left() {
    let lcd = setup_display();

    lcd.shift(ShiftTo::Left(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_1000));
}

#[test]
fn test_shift_right() {
    let lcd = setup_display();

    lcd.shift(ShiftTo::Right(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_1100));
}

#[test]
fn test_clear() {
    let lcd = setup_display();

    lcd.clear();

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0x01));
}

#[test]
fn test_seek_from_home() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(3));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b1000_0011));
}

#[test]
fn test_seek_from_current() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(2));
    lcd.seek(SeekFrom::Current(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b1000_0010));
    assert_eq!(outp[1], to_level_slice(0b1000_0011));
}

#[test]
fn test_seek_from_line() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Line {
        line: DefaultLines::Two,
        bytes: 3,
    });

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b1100_0011));
}

#[test]
fn test_seek_cgram_from_home() {
    let mut lcd = setup_display();

    lcd.seek_cgram(SeekFrom::Home(3));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0100_0011));
}

#[test]
fn test_seek_cgram_from_current() {
    let mut lcd = setup_display();

    lcd.seek_cgram(SeekFrom::Home(2));
    lcd.seek_cgram(SeekFrom::Current(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0100_0010));
    assert_eq!(outp[1], to_level_slice(0b0100_0011));
}

#[test]
#[ignore]
// TODO: needs clarification: line does not make sense here,  For 5×8 dots, eight character
// patterns can be written, and for 5×10 dots, four character patterns can be written
fn test_seek_cgram_from_line() {
    let mut lcd = setup_display();

    lcd.seek_cgram(SeekFrom::Home(2));
    lcd.seek_cgram(SeekFrom::Current(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0100_0010));
    assert_eq!(outp[1], to_level_slice(0b0100_0011));
}

#[test]
fn test_write() {
    let mut lcd = setup_display();

    lcd.write(123);

    let pins = lcd.get_pins();

    {
        let actual = pins.read.levels.borrow();
        assert_eq!(actual[..], [Level::Low]);

        let actual = pins.register_select.levels.borrow();
        assert_eq!(actual[..], [Level::High]);
    }

    let outp = flat_pins(pins);
    assert_eq!(outp[0], to_level_slice(123));
}

#[test]
fn test_write_updates_address_counter() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(0));
    lcd.write(12);
    lcd.write(34);
    lcd.seek(SeekFrom::Current(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);
    assert_eq!(outp[3], to_level_slice(0b1000_0010));
}

#[test]
fn test_write_message() {
    let mut lcd = setup_display();

    lcd.write_message("Hi");

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);
    assert_eq!(outp[0], to_level_slice(b'H'));
    assert_eq!(outp[1], to_level_slice(b'i'));
}

#[test]
fn test_write_message_increments_address_counter() {
    let mut lcd = setup_display();

    lcd.write_message("Hi");
    lcd.seek(SeekFrom::Current(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);
    assert_eq!(outp[2], to_level_slice(0b1000_0010));
}

#[test]
fn test_read() {
    let mut pins = DisplayPins {
        register_select: PinMock::default(),
        read: PinMock::default(),
        enable: PinMock::default(),
        data4: PinMock::default(),
        data5: PinMock::default(),
        data6: PinMock::default(),
        data7: PinMock::default(),
    };

    let expected = 42;
    set_read_value(&mut pins, expected);

    let mut lcd = Display::<PinMock, DefaultLines, CustomDelayMock>::from_pins(pins);

    let input = lcd.read_byte();

    assert_eq!(input, expected);
}

#[test]
fn test_read_increments_address_counter() {
    let mut pins = DisplayPins {
        register_select: PinMock::default(),
        read: PinMock::default(),
        enable: PinMock::default(),
        data4: PinMock::default(),
        data5: PinMock::default(),
        data6: PinMock::default(),
        data7: PinMock::default(),
    };

    set_read_value(&mut pins, 4);
    set_read_value(&mut pins, 2);

    let mut lcd = Display::<PinMock, DefaultLines, CustomDelayMock>::from_pins(pins);

    lcd.read_byte();
    lcd.seek(SeekFrom::Current(0));

    lcd.read_byte();
    lcd.seek(SeekFrom::Current(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);
    assert_eq!(outp[0], to_level_slice(0b1000_0001));
    assert_eq!(outp[1], to_level_slice(0b1000_0010));
}

fn set_read_value(pins: &mut DisplayPins<PinMock>, value: u8) {
    pins.data4.set_value((value & 0b0001_0000) >> 4);
    pins.data5.set_value((value & 0b0010_0000) >> 5);
    pins.data6.set_value((value & 0b0100_0000) >> 6);
    pins.data7.set_value((value & 0b1000_0000) >> 7);

    pins.data4.set_value(value & 0b0000_0001);
    pins.data5.set_value((value & 0b0000_0010) >> 1);
    pins.data6.set_value((value & 0b0000_0100) >> 2);
    pins.data7.set_value((value & 0b0000_1000) >> 3);
}

fn flat_pins(pins: DisplayPins<PinMock>) -> Vec<Vec<Level>> {
    let mut r = vec![];

    let data4 = pins.data4.levels.into_inner();
    let data5 = pins.data5.levels.into_inner();
    let data6 = pins.data6.levels.into_inner();
    let data7 = pins.data7.levels.into_inner();

    for (d4, d5, d6, d7) in multizip((data4, data5, data6, data7)) {
        r.push(vec![d7, d6, d5, d4]);
    }

    r.into_iter()
        .chunks(2)
        .into_iter()
        .map(|u| u.into_iter().flat_map(|v| v).collect::<Vec<_>>())
        .collect::<Vec<_>>()
}

fn to_level_slice(v: u8) -> Vec<Level> {
    let mut l: Vec<Level> = Vec::new();

    for i in (0..8).rev() {
        if (v & (1 << i)) > 0 {
            l.push(Level::High);
        } else {
            l.push(Level::Low);
        }
    }

    l
}
<commit_msg>Fix tests<commit_after>extern crate clerk;
extern crate itertools;

use std::cell::RefCell;
use std::collections::VecDeque;
use itertools::{multizip, Itertools};

use clerk::{DataPins4Lines, DefaultLines, Delay, Direction, Display, DisplayControlBuilder,
            DisplayHardwareLayer, EntryModeBuilder, FunctionSetBuilder, Level, Pins, SeekFrom,
            ShiftTo};

pub struct PinMock {
    levels: RefCell<Vec<Level>>,
    directions: RefCell<Vec<Direction>>,
    values: Option<RefCell<VecDeque<u8>>>,
}

impl Default for PinMock {
    fn default() -> Self {
        PinMock {
            values: Some(RefCell::new(VecDeque::new())),
            levels: RefCell::new(vec![]),
            directions: RefCell::new(vec![]),
        }
    }
}

impl PinMock {
    pub fn set_value(&mut self, value: u8) {
        match self.values {
            Some(ref mut v) => v.borrow_mut().push_back(value),
            None => (),
        }
    }
}

impl DisplayHardwareLayer for PinMock {
    fn init(&self) {}

    fn cleanup(&self) {}

    fn set_direction(&self, direction: Direction) {
        let mut directions = self.directions.borrow_mut();

        directions.push(direction);
    }

    fn set_level(&self, level: Level) {
        let mut levels = self.levels.borrow_mut();

        levels.push(level);
    }

    fn get_value(&self) -> u8 {
        match self.values {
            Some(ref v) => v.borrow_mut().pop_front().unwrap(),
            None => panic!("No return value specified for current test."),
        }
    }
}

pub struct CustomDelayMock;

impl Delay for CustomDelayMock {
    fn delay_ns(_: u16) {
        // mhh
    }
}

fn setup_display() -> Display<
    Pins<PinMock, PinMock, PinMock, DataPins4Lines<PinMock, PinMock, PinMock, PinMock>>,
    DefaultLines,
> {
    let pins = Pins {
        register_select: PinMock::default(),
        read: PinMock::default(),
        enable: PinMock::default(),
        data: DataPins4Lines {
            data4: PinMock::default(),
            data5: PinMock::default(),
            data6: PinMock::default(),
            data7: PinMock::default(),
        },
    };

    Display::from_pins(pins)
}

#[test]
fn test_init() {
    let lcd = setup_display();

    lcd.init(&FunctionSetBuilder::default());

    let pins = lcd.get_pins();
    {
        // check initialization
        let actual = pins.register_select.directions.borrow();
        assert_eq!(actual[..], [Direction::Out]);

        let actual = pins.read.directions.borrow();
        assert_eq!(actual[..], [Direction::Out]);

        let actual = pins.enable.directions.borrow();
        assert_eq!(actual[..], [Direction::Out]);
    }

    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0x33));
    assert_eq!(outp[1], to_level_slice(0x32));
    assert_eq!(outp[2], to_level_slice(0x20));
    assert_eq!(outp[3], to_level_slice(0x01));
}

#[test]
fn test_set_entry_mode() {
    let lcd = setup_display();

    lcd.set_entry_mode(&EntryModeBuilder::default());

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0000_0110));
}

#[test]
fn test_set_display_control() {
    let lcd = setup_display();

    lcd.set_display_control(&DisplayControlBuilder::default());

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0000_1100));
}

#[test]
fn test_shift_cursor_left() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Left(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_0000));
}

#[test]
fn test_shift_cursor_left_with_zero_offset() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Left(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp.len(), 0);
}

#[test]
fn test_shift_cursor_right() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_0100));
}

#[test]
fn test_shift_cursor_right_multiple() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(2));
    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_0100));
    assert_eq!(outp[1], to_level_slice(0b0001_0100));
}

#[test]
fn test_shift_cursor_right_with_zero_offset() {
    let mut lcd = setup_display();

    lcd.shift_cursor(ShiftTo::Right(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp.len(), 0);
}

#[test]
fn test_shift_left() {
    let lcd = setup_display();

    lcd.shift(ShiftTo::Left(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_1000));
}

#[test]
fn test_shift_right() {
    let lcd = setup_display();

    lcd.shift(ShiftTo::Right(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0001_1100));
}

#[test]
fn test_clear() {
    let lcd = setup_display();

    lcd.clear();

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0x01));
}

#[test]
fn test_seek_from_home() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(3));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b1000_0011));
}

#[test]
fn test_seek_from_current() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(2));
    lcd.seek(SeekFrom::Current(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b1000_0010));
    assert_eq!(outp[1], to_level_slice(0b1000_0011));
}

#[test]
fn test_seek_from_line() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Line {
        line: DefaultLines::Two,
        bytes: 3,
    });

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b1100_0011));
}

#[test]
fn test_seek_cgram_from_home() {
    let mut lcd = setup_display();

    lcd.seek_cgram(SeekFrom::Home(3));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0100_0011));
}

#[test]
fn test_seek_cgram_from_current() {
    let mut lcd = setup_display();

    lcd.seek_cgram(SeekFrom::Home(2));
    lcd.seek_cgram(SeekFrom::Current(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0100_0010));
    assert_eq!(outp[1], to_level_slice(0b0100_0011));
}

#[test]
#[ignore]
// TODO: needs clarification: line does not make sense here,  For 5×8 dots, eight character
// patterns can be written, and for 5×10 dots, four character patterns can be written
fn test_seek_cgram_from_line() {
    let mut lcd = setup_display();

    lcd.seek_cgram(SeekFrom::Home(2));
    lcd.seek_cgram(SeekFrom::Current(1));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);

    assert_eq!(outp[0], to_level_slice(0b0100_0010));
    assert_eq!(outp[1], to_level_slice(0b0100_0011));
}

#[test]
fn test_write() {
    let mut lcd = setup_display();

    lcd.write(123);

    let pins = lcd.get_pins();

    {
        let actual = pins.read.levels.borrow();
        assert_eq!(actual[..], [Level::Low]);

        let actual = pins.register_select.levels.borrow();
        assert_eq!(actual[..], [Level::High]);
    }

    let outp = flat_pins(pins);
    assert_eq!(outp[0], to_level_slice(123));
}

#[test]
fn test_write_updates_address_counter() {
    let mut lcd = setup_display();

    lcd.seek(SeekFrom::Home(0));
    lcd.write(12);
    lcd.write(34);
    lcd.seek(SeekFrom::Current(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);
    assert_eq!(outp[3], to_level_slice(0b1000_0010));
}

#[test]
fn test_write_message() {
    let mut lcd = setup_display();

    lcd.write_message("Hi");

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);
    assert_eq!(outp[0], to_level_slice(b'H'));
    assert_eq!(outp[1], to_level_slice(b'i'));
}

#[test]
fn test_write_message_increments_address_counter() {
    let mut lcd = setup_display();

    lcd.write_message("Hi");
    lcd.seek(SeekFrom::Current(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);
    assert_eq!(outp[2], to_level_slice(0b1000_0010));
}

#[test]
fn test_read() {
    let mut pins = Pins {
        register_select: PinMock::default(),
        read: PinMock::default(),
        enable: PinMock::default(),
        data: DataPins4Lines {
            data4: PinMock::default(),
            data5: PinMock::default(),
            data6: PinMock::default(),
            data7: PinMock::default(),
        },
    };

    let expected = 42;
    set_read_value(&mut pins, expected);

    let mut lcd = Display::<_, DefaultLines>::from_pins(pins);

    let input = lcd.read_byte();

    assert_eq!(input, expected);
}

#[test]
fn test_read_increments_address_counter() {
    let mut pins = Pins {
        register_select: PinMock::default(),
        read: PinMock::default(),
        enable: PinMock::default(),
        data: DataPins4Lines {
            data4: PinMock::default(),
            data5: PinMock::default(),
            data6: PinMock::default(),
            data7: PinMock::default(),
        },
    };

    set_read_value(&mut pins, 4);
    set_read_value(&mut pins, 2);

    let mut lcd = Display::<_, DefaultLines>::from_pins(pins);

    lcd.read_byte();
    lcd.seek(SeekFrom::Current(0));

    lcd.read_byte();
    lcd.seek(SeekFrom::Current(0));

    let pins = lcd.get_pins();
    let outp = flat_pins(pins);
    assert_eq!(outp[0], to_level_slice(0b1000_0001));
    assert_eq!(outp[1], to_level_slice(0b1000_0010));
}

fn set_read_value<RS, R, E>(
    pins: &mut Pins<RS, R, E, DataPins4Lines<PinMock, PinMock, PinMock, PinMock>>,
    value: u8,
) {
    pins.data.data4.set_value((value & 0b0001_0000) >> 4);
    pins.data.data5.set_value((value & 0b0010_0000) >> 5);
    pins.data.data6.set_value((value & 0b0100_0000) >> 6);
    pins.data.data7.set_value((value & 0b1000_0000) >> 7);

    pins.data.data4.set_value(value & 0b0000_0001);
    pins.data.data5.set_value((value & 0b0000_0010) >> 1);
    pins.data.data6.set_value((value & 0b0000_0100) >> 2);
    pins.data.data7.set_value((value & 0b0000_1000) >> 3);
}

fn flat_pins<RS, R, E>(
    pins: Pins<RS, R, E, DataPins4Lines<PinMock, PinMock, PinMock, PinMock>>,
) -> Vec<Vec<Level>> {
    let mut r = vec![];

    let data4 = pins.data.data4.levels.into_inner();
    let data5 = pins.data.data5.levels.into_inner();
    let data6 = pins.data.data6.levels.into_inner();
    let data7 = pins.data.data7.levels.into_inner();

    for (d4, d5, d6, d7) in multizip((data4, data5, data6, data7)) {
        r.push(vec![d7, d6, d5, d4]);
    }

    r.into_iter()
        .chunks(2)
        .into_iter()
        .map(|u| u.into_iter().flat_map(|v| v).collect::<Vec<_>>())
        .collect::<Vec<_>>()
}

fn to_level_slice(v: u8) -> Vec<Level> {
    let mut l: Vec<Level> = Vec::new();

    for i in (0..8).rev() {
        if (v & (1 << i)) > 0 {
            l.push(Level::High);
        } else {
            l.push(Level::Low);
        }
    }

    l
}
<|endoftext|>


<commit_before>#![feature(plugin)]

extern crate "rustc-serialize" as rustc_serialize;

extern crate docopt;

#[plugin]
#[no_link]
extern crate docopt_macros;

extern crate rustscripts;

#[warn(non_camel_case_types)]
#[warn(non_snake_case)]
#[warn(unused_qualifications)]
#[warn(non_upper_case_globals)]
#[warn(missing_docs)]

/** Creates fake words.
 *  First, it uses a predefined list of words to generate a markov chain
 *  for word prefixes; it then probabilistically follows this to generate
 *  fake words.
**/

use docopt::Docopt;

use std::collections::{HashSet,HashMap};
use std::rand;
use std::rand::Rng;
use std::io::{File,BufferedReader};
use std::slice::AsSlice;
//use std::iter::{FromIterator,IteratorExt};

use rustscripts::counter;

//* Build a HashMap of "substring" : "number of occurrences"
fn to_hashes(wordlist : &[String], sublens : u32) -> HashMap<String, u32> {
	println!("Got wordlist, length {}", wordlist.len());

	let iter = wordlist.iter().filter(|k| {
		if k.chars().count() == 0 {false}
		else if k.contains("\'") {false}
		else if k.find(|&: c : char|{!c.is_lowercase()}).is_some() {false}
		else {true}
	});
	let trimchars : &[char] = &[' ', '\t', '\r', '\n'];
	let iter = iter.flat_map(|k| {
		let kslice = k.as_slice();
		let fullword : String = (["^", kslice.trim_matches(trimchars), "$"]).concat();
        let fullchars : Vec<char> = fullword.chars().collect();

        let chvec : Vec<String> =
		fullchars.windows(sublens as usize).map(|chars| {
			chars.iter().map(|&x| x).collect::<String>()
		})
		.collect();

		//~ println!("k: {}, chvec: {}", k, chvec);

		chvec.into_iter()
	});
	counter(iter)
}

struct WordBuilder {
    subs : HashMap<String, u32>,
    // list : Vec<String>,
    // sublens : u32,
    wordset : HashSet<String>,
    wordlens : Vec<u32>
}

struct WordIter<'a> {
    p : &'a mut WordBuilder
}

impl WordBuilder {
    fn new(list : Vec<String>, sublens : u32) -> WordBuilder {
        let mut h = HashSet::new();
        let mut wlens : Vec<u32> = Vec::new();
        for w in list.iter() {
            h.insert(w.to_string());
            let wordlen = w.len();
            for _ in range(wlens.len(), wordlen+1){
                wlens.push(0);
            }
            wlens[wordlen] += 1;
        }

        WordBuilder {
            subs : to_hashes(list.as_slice(), sublens),
            //list : list,
            //sublens : sublens,
            wordset : h,
            wordlens : wlens
        }
    }

    fn word(&mut self) -> Option<String> {
        let mut s : String = "^".to_string();

        loop {
            let mut fullsum = 0u32;
            let mut endsum = 0u32;
            let possibilities : Vec<(&str, u32)> = self.subs.iter().filter_map(
                |(k,v)| {
                    /* the beginning of k and the end of s must match for
                     * k to be a possibility
                     * if s is long, then the first (klength - 1) letters
                     * of k must match the last (klength - 1) letters of s
                     * otherwise, the first (slength) characters
                    */
                    let slen = s.as_slice().chars().count();
                    let kslice = k.as_slice();
                    let klen = kslice.chars().count();
                    let kcut = if slen < klen - 1 {slen} else {klen - 1};
                    if s.as_slice().ends_with(kslice.slice_chars(0, kcut)){
                        fullsum += *v;
                        if kslice.ends_with("$") {endsum += *v;}
                        Some((kslice,*v))
                    } else {None}
                }
            ).collect();
            if fullsum == 0 {
                panic!("s: \"{}\"", s);
            }

            let endprob = if self.wordlens.len() > s.len() {
                let wordlenslice : &[u32] = self.wordlens.slice(s.len()-1, self.wordlens.len());
                let c = wordlenslice[0];
                let l = wordlenslice.iter().fold(0, |a,&b|{a+b});
                (c as f64) / (l as f64)
            } else {
                //~ println!("Too long: {}", s);
                return None;
            };

            let randnum = rand::thread_rng().gen_range(0.0, 1.0);

            let endtime = randnum < endprob;
            if (endtime && (endsum == 0)) || ((!endtime) && (fullsum-endsum==0)) {
                //~ println!("Failed to end: {}", s);
                return None;
            }

            //~ println!("endtime: {} {} : ({},{}) {}", endtime, randnum < endprob,
                //~ endsum, fullsum, if(endtime){endsum} else {fullsum - endsum});
            let randnum = rand::thread_rng().gen_range(0.0,
                (if endtime {endsum} else {fullsum - endsum} as f64));

            let mut psum = 0;

            for &(k,v) in possibilities.iter() {
                if endtime ^ k.ends_with("$") {continue;};

                if randnum < ((psum + v) as f64) {
                    let slen = s.as_slice().chars().count();
                    let klen = k.chars().count();
                    let kcut = if slen < klen - 1 {slen} else {klen - 1};
                    //~ let olds = s.to_string();
                    s.push_str(k.slice_chars(kcut,klen));
                    break;
                }
                psum += v;
            }

            let slen = s.as_slice().chars().count();
            if s.as_slice().slice_chars(slen-1, slen) == "$" {
                return Some(s.as_slice().slice_chars(1, slen-1).to_string());
            }
        };
    }

    fn iter<'a>(&'a mut self) -> WordIter<'a> {
        WordIter{p:self}
    }
}

impl<'a> Iterator for WordIter<'a> {
    type Item = String;

    fn next(&mut self) -> Option<String> {
        loop {
            let optw = self.p.word();
            match optw {
                None => {},
                Some(w) => {
                    if !self.p.wordset.contains(&w)
                        {return Some(w)};
                }
            };
        }
    }
}

docopt!(Args, "
Usage: fakewords2 [-h | --help] [-n <number>] [<dictfile>]

Options:
    -n <number>    use number length substrings for markovian chain
    <dictfile>     use dictfile instead of /usr/share/dict/words
", flag_n : Option<u32>, arg_dictfile : Option<String>);

pub fn main(){
	let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());

    let flag_n : Option<u32> = args.flag_n;

    let subsetn : u32 = flag_n.unwrap_or(4);

    let pathstr = args.arg_dictfile.map(|s| {s}).unwrap_or("/usr/share/dict/words".to_string());

    let path = Path::new(pathstr.clone());
    let file = match File::open(&path) {
		Ok(f) => f,
		Err(std::io::IoError{kind: std::io::FileNotFound, desc: _, detail: _}) => {
			let _ = writeln!(&mut std::io::stderr(), "File not found: {}", pathstr);
			std::os::set_exit_status(-1);
			return;
		}
		Err(e) => panic!("failed to open file: {}", e)
	};

    let mut file = BufferedReader::new(file);

    let trimchars : &[char] = &[' ', '\t', '\r', '\n'];

    let lines: Vec<String> = file.lines().map(|orl| {
		let unwrapl : String = match orl {
			Ok(l) => l,
			Err(e) => panic!("Failed reading file: {}", e)
		};
		unwrapl.trim_matches(trimchars).to_string()
	}).collect();
    let mut wb = WordBuilder::new(lines, subsetn);

    println!("Now have a map of length {}", wb.subs.len());

    for w in wb.iter(){
        println!("{}",w);
    };
}
<commit_msg>Updated to newest Rust<commit_after>#![feature(plugin)]

extern crate "rustc-serialize" as rustc_serialize;

extern crate docopt;

#[plugin]
#[no_link]
extern crate docopt_macros;

extern crate rustscripts;

#[warn(non_camel_case_types)]
#[warn(non_snake_case)]
#[warn(unused_qualifications)]
#[warn(non_upper_case_globals)]
#[warn(missing_docs)]

/** Creates fake words.
 *  First, it uses a predefined list of words to generate a markov chain
 *  for word prefixes; it then probabilistically follows this to generate
 *  fake words.
**/

use docopt::Docopt;

use std::collections::{HashSet,HashMap};
use std::rand;
use std::rand::Rng;
use std::old_io::{File,BufferedReader};
use std::slice::AsSlice;
//use std::iter::{FromIterator,IteratorExt};

use rustscripts::counter;

//* Build a HashMap of "substring" : "number of occurrences"
fn to_hashes(wordlist : &[String], sublens : u32) -> HashMap<String, u32> {
	println!("Got wordlist, length {}", wordlist.len());

	let iter = wordlist.iter().filter(|k| {
		if k.chars().count() == 0 {false}
		else if k.contains("\'") {false}
		else if k.find(|&: c : char|{!c.is_lowercase()}).is_some() {false}
		else {true}
	});
	let trimchars : &[char] = &[' ', '\t', '\r', '\n'];
	let iter = iter.flat_map(|k| {
		let kslice = k.as_slice();
		let fullword : String = (["^", kslice.trim_matches(trimchars), "$"]).concat();
        let fullchars : Vec<char> = fullword.chars().collect();

        let chvec : Vec<String> =
		fullchars.windows(sublens as usize).map(|chars| {
			chars.iter().map(|&x| x).collect::<String>()
		})
		.collect();

		//~ println!("k: {}, chvec: {}", k, chvec);

		chvec.into_iter()
	});
	counter(iter)
}

struct WordBuilder {
    subs : HashMap<String, u32>,
    // list : Vec<String>,
    // sublens : u32,
    wordset : HashSet<String>,
    wordlens : Vec<u32>
}

struct WordIter<'a> {
    p : &'a mut WordBuilder
}

impl WordBuilder {
    fn new(list : Vec<String>, sublens : u32) -> WordBuilder {
        let mut h = HashSet::new();
        let mut wlens : Vec<u32> = Vec::new();
        for w in list.iter() {
            h.insert(w.to_string());
            let wordlen = w.len();
            for _ in range(wlens.len(), wordlen+1){
                wlens.push(0);
            }
            wlens[wordlen] += 1;
        }

        WordBuilder {
            subs : to_hashes(list.as_slice(), sublens),
            //list : list,
            //sublens : sublens,
            wordset : h,
            wordlens : wlens
        }
    }

    fn word(&mut self) -> Option<String> {
        let mut s : String = "^".to_string();

        loop {
            let mut fullsum = 0u32;
            let mut endsum = 0u32;
            let possibilities : Vec<(&str, u32)> = self.subs.iter().filter_map(
                |(k,v)| {
                    /* the beginning of k and the end of s must match for
                     * k to be a possibility
                     * if s is long, then the first (klength - 1) letters
                     * of k must match the last (klength - 1) letters of s
                     * otherwise, the first (slength) characters
                    */
                    let slen = s.as_slice().chars().count();
                    let kslice = k.as_slice();
                    let klen = kslice.chars().count();
                    let kcut = if slen < klen - 1 {slen} else {klen - 1};
                    if s.as_slice().ends_with(kslice.slice_chars(0, kcut)){
                        fullsum += *v;
                        if kslice.ends_with("$") {endsum += *v;}
                        Some((kslice,*v))
                    } else {None}
                }
            ).collect();
            if fullsum == 0 {
                panic!("s: \"{}\"", s);
            }

            let endprob = if self.wordlens.len() > s.len() {
                let wordlenslice : &[u32] = &(self.wordlens[(s.len()-1)..self.wordlens.len()]);
                let c = wordlenslice[0];
                let l = wordlenslice.iter().fold(0, |a,&b|{a+b});
                (c as f64) / (l as f64)
            } else {
                //~ println!("Too long: {}", s);
                return None;
            };

            let randnum = rand::thread_rng().gen_range(0.0, 1.0);

            let endtime = randnum < endprob;
            if (endtime && (endsum == 0)) || ((!endtime) && (fullsum-endsum==0)) {
                //~ println!("Failed to end: {}", s);
                return None;
            }

            //~ println!("endtime: {} {} : ({},{}) {}", endtime, randnum < endprob,
                //~ endsum, fullsum, if(endtime){endsum} else {fullsum - endsum});
            let randnum = rand::thread_rng().gen_range(0.0,
                (if endtime {endsum} else {fullsum - endsum} as f64));

            let mut psum = 0;

            for &(k,v) in possibilities.iter() {
                if endtime ^ k.ends_with("$") {continue;};

                if randnum < ((psum + v) as f64) {
                    let slen = s.as_slice().chars().count();
                    let klen = k.chars().count();
                    let kcut = if slen < klen - 1 {slen} else {klen - 1};
                    //~ let olds = s.to_string();
                    s.push_str(k.slice_chars(kcut,klen));
                    break;
                }
                psum += v;
            }

            let slen = s.as_slice().chars().count();
            if s.as_slice().slice_chars(slen-1, slen) == "$" {
                return Some(s.as_slice().slice_chars(1, slen-1).to_string());
            }
        };
    }

    fn iter<'a>(&'a mut self) -> WordIter<'a> {
        WordIter{p:self}
    }
}

impl<'a> Iterator for WordIter<'a> {
    type Item = String;

    fn next(&mut self) -> Option<String> {
        loop {
            let optw = self.p.word();
            match optw {
                None => {},
                Some(w) => {
                    if !self.p.wordset.contains(&w)
                        {return Some(w)};
                }
            };
        }
    }
}

docopt!(Args, "
Usage: fakewords2 [-h | --help] [-n <number>] [<dictfile>]

Options:
    -n <number>    use number length substrings for markovian chain
    <dictfile>     use dictfile instead of /usr/share/dict/words
", flag_n : Option<u32>, arg_dictfile : Option<String>);

pub fn main(){
	let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());

    let flag_n : Option<u32> = args.flag_n;

    let subsetn : u32 = flag_n.unwrap_or(4);

    let pathstr = args.arg_dictfile.map(|s| {s}).unwrap_or("/usr/share/dict/words".to_string());

    let path = Path::new(pathstr.clone());
    let file = match File::open(&path) {
		Ok(f) => f,
		Err(std::old_io::IoError{kind: std::old_io::FileNotFound, desc: _, detail: _}) => {
			let _ = writeln!(&mut std::old_io::stderr(), "File not found: {}", pathstr);
			std::os::set_exit_status(-1);
			return;
		}
		Err(e) => panic!("failed to open file: {}", e)
	};

    let mut file = BufferedReader::new(file);

    let trimchars : &[char] = &[' ', '\t', '\r', '\n'];

    let lines: Vec<String> = file.lines().map(|orl| {
		let unwrapl : String = match orl {
			Ok(l) => l,
			Err(e) => panic!("Failed reading file: {}", e)
		};
		unwrapl.trim_matches(trimchars).to_string()
	}).collect();
    let mut wb = WordBuilder::new(lines, subsetn);

    println!("Now have a map of length {}", wb.subs.len());

    for w in wb.iter(){
        println!("{}",w);
    };
}
<|endoftext|>


<commit_before>//! The module contains implementations of the transport layer functionality
//! that HTTP/2 requires. It exposes APIs that allow the HTTP/2 connection to
//! use the transport layer without requiring it to know which exact
//! implementation they are using (i.e. a clear-text TCP connection, a TLS
//! protected connection, or even a mock implementation).

use std::old_io::Stream;

/// A trait that any struct that wants to provide the transport layer for
/// HTTP/2 needs to implement.
///
/// For now, we do not define any additional methods on top of those required
/// by the `Stream` trait.
pub trait TransportStream: Stream {}
<commit_msg>Implement the TransportStream trait for TcpStream<commit_after>//! The module contains implementations of the transport layer functionality
//! that HTTP/2 requires. It exposes APIs that allow the HTTP/2 connection to
//! use the transport layer without requiring it to know which exact
//! implementation they are using (i.e. a clear-text TCP connection, a TLS
//! protected connection, or even a mock implementation).

use std::old_io::Stream;
use std::old_io::net::tcp::TcpStream;

/// A trait that any struct that wants to provide the transport layer for
/// HTTP/2 needs to implement.
///
/// For now, we do not define any additional methods on top of those required
/// by the `Stream` trait.
pub trait TransportStream: Stream {}

/// Since `TcpStream` already implements `Stream` and we do not define any
/// additional required methods on `TransportStream`, we get this for free.
impl TransportStream for TcpStream {}
<|endoftext|>