<commit_before>// (c) 2015 Joost Yervante Damad <joost@damad.be>

// loosely based on https://github.com/cgaebel/sexp
// latest version can be found at https://github.com/andete/rust_sexp

use std::fmt;
use std::str::FromStr;
use std::f64;

use std::fs::File;
use std::io::prelude::*;

pub enum Atom {
  S(String),
  Q(String),
  I(i64),
  F(f64),
}

impl Atom {
    pub fn f(&self) -> Result<f64,String> {
        match *self {
            Atom::F(f) => Ok(f),
            _ => Err(String::from("not a float"))
        }
    }
    pub fn i(&self) -> Result<i64,String> {
        match *self {
            Atom::I(i) => Ok(i),
            _ => Err(String::from("not an int"))
        }
    }
}

pub enum Sexp {
  Atom(Atom),
  Empty,
  List(Vec<Sexp>),
}

impl Sexp {
    pub fn atom(&self) -> Result<&Atom,String> {
        match *self {
            Sexp::Atom(ref f) => Ok(f),
            _ => Err(String::from("not an atom"))
        }
    }
    pub fn list(&self) -> Result<&Vec<Sexp>,String> {
        match *self {
            Sexp::List(ref v) => Ok(v),
            _ => Err(String::from("not a list"))
        }
    }
}

pub struct ParseError {
    msg: &'static str,
    line: usize,
    lpos: usize,
}

pub enum Error {
    ParseError(ParseError),
    IOError(std::io::Error),
}

pub type ERes<T> = Result<T, Error>;

fn err_parse<T>(msg: &'static str, state: &ParseState) -> ERes<T> {
    Err(Error::ParseError(ParseError { msg: msg, line: state.line, lpos: state.lpos}))
}

fn err_io<T>(err: std::io::Error) -> ERes<T> {
    Err(Error::IOError(err))
}

struct ParseState {
    pos: usize,
    line: usize,
    lpos: usize,
    vec: Vec<char>,
    len: usize,
}

impl ParseState {
    fn peek(&self) -> ERes<char> {
        if self.pos >= self.len {
            return err_parse("end of document reached", self)
        }
        Ok(self.vec[self.pos])
    }
    fn peek_option(&self) -> Option<char> {
        if self.pos >= self.len {
            return None
        }
        Some(self.vec[self.pos])
    }
    fn next(&mut self) -> ERes<()> {
        if self.pos >= self.len {
            return err_parse("end of document reached", self)
        }
        let c = self.vec[self.pos];
        self.pos += 1;
        match c {
            '\r' | '\n' => {
                self.lpos = 0;
                self.line += 1;
            }
            _ => {
                self.lpos += 1;
            }
        }
        Ok(())
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::ParseError(ref p) =>
                write!(f, "Parse Error {}:{}: {}", p.line, p.lpos, p.msg),
            Error::IOError(ref i) =>
                write!(f, "{}", i)
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::ParseError(ref p) =>
                write!(f, "Parse Error {}:{}: {}", p.line, p.lpos, p.msg),
            Error::IOError(ref i) =>
                write!(f, "{}", i)
        }
    }
}

impl fmt::Display for Atom {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    match *self {
        Atom::S(ref s) => write!(f, "{}", s),
        Atom::Q(ref s) => write!(f, "\"{}\"", s),
        Atom::I(i)     => write!(f, "{}", i),
        Atom::F(d)     => {
            let z = d.floor();
            if d - z < f64::EPSILON {
                write!(f, "{}.0", z)
            } else {
                write!(f, "{}", d)
            }
        }
    }
  }
}

impl fmt::Display for Sexp {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    match *self {
      Sexp::Atom(ref a) => write!(f, "{}", a),
      Sexp::List(ref xs) => {
        try!(write!(f, "("));
        for (i, x) in xs.iter().enumerate() {
          let s = if i == 0 { "" } else { " " };
          try!(write!(f, "{}{}", s, x));
        }
        write!(f, ")")
      },
      Sexp::Empty => write!(f, ""),
    }
  }
}

fn parse_list(state: &mut ParseState) -> ERes<Sexp> {
    //println!("list");
    try!(state.next()); // skip (
    let mut l: Vec<Sexp> = Vec::new();
    loop {
        match try!(state.peek()) {
            ')' => {
                try!(state.next());
                break;
            }
            _ => {
                l.push(try!(parse_sexp(state)));
            }
        }
    }
    //println!("Found list");
    Ok(Sexp::List(l))
}

fn parse_quoted_string(state: &mut ParseState) -> ERes<Atom> {
    //println!("qstring");
    try!(state.next()); // skip "
    let mut s = String::new();
    loop {
        match try!(state.peek()) {
            '"' => {
                try!(state.next());
                break
            }
            x @ '\r' | x @ '\n' => {
                s.push(x);
                try!(state.next());
                }
            x => {
                s.push(x);
                try!(state.next());
            }
        }
    }
    //println!("Found quoted string {}", s);
    Ok(Atom::Q(s))
}

fn parse_string(state: &mut ParseState) -> ERes<Atom> {
    //println!("string");
    let mut s = String::new();
    loop {
        match state.peek_option() {
            Some(x) => {
                match x {
                    ' ' | '\t' | '\r' | '\n' | ')' => break,
                    '"' => return err_parse("unexpected \" in string", state),
                    x => s.push(x),
                }
            }
            None => {
                break;
            }
        }
        try!(state.next())
    }
    //println!("Found string {}", s);
    Ok(Atom::S(s))
}

fn parse_number(state: &mut ParseState) -> ERes<Atom> {
    //println!("number");
    let mut s = String::new();
    loop {
        match state.peek_option() {
            Some(x) => {
                match x {
                    ' ' | '\r' | '\n' | '\t' | ')' => {
                        break
                    },
                    '0' ... '9' | '.' | '-' => {
                        s.push(state.vec[state.pos])
                    },
                    _ => {
                        return err_parse("unexpected char in number", state)
                    },
                }
            }
            None => {
                break
            }
        }
        try!(state.next())
    }
    //println!("Found number {}", s);
    let s2: &str = &s[..];
    if s.contains('.') {
        Ok(Atom::F(f64::from_str(s2).unwrap()))
    } else {
        Ok(Atom::I(i64::from_str(s2).unwrap()))
    }
}

fn parse_atom(state: &mut ParseState) -> ERes<Sexp> {
    //println!("atom");
    let a = match try!(state.peek()) {
        '"' => {
            try!(parse_quoted_string(state))
        }
        '0' ... '9' | '.' | '-' => {
            try!(parse_number(state))
        }
        _ => {
            try!(parse_string(state))
        }
    };
    Ok(Sexp::Atom(a))
}

fn parse_sexp(state: &mut ParseState) -> ERes<Sexp> {
    loop {
        match try!(state.peek()) {
            '(' => {
                return parse_list(state)
            }
            ' ' | '\t' | '\r' | '\n' => {
                try!(state.next());
            }
            ')' => {
                return err_parse("unmatched )", state)
            }
            _ => {
                return parse_atom(state)
            }
        }
    }
}

fn parse(data: &str) -> ERes<Sexp> {
    if data.len() == 0 {
        Ok(Sexp::Empty)
    } else {
        let vec: Vec<char> = data.chars().collect();
        let len = vec.len();
        let state = &mut ParseState { pos: 0, line: 1, lpos: 0, vec: vec, len: len };
        parse_sexp(state)
    }
}

fn read_file(name: &str) -> Result<String, std::io::Error> {
    let mut f = try!(File::open(name));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));
    Ok(s)
}

pub fn parse_str(s: &str) -> ERes<Sexp> {
    parse(s)
}

pub fn parse_file(name: &str) -> ERes<Sexp> {
    let s = try!(match read_file(name) {
        Ok(s) => Ok(s),
        Err(x) => err_io(x),
    });
    parse(&s[..])
}

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

    #[allow(dead_code)]
    fn check_parse(s: &str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(s, t);
    }

    #[allow(dead_code)]
    fn parse_fail(s: &str) {
        parse_str(s).unwrap();
    }

    #[test]
    fn test_empty() { check_parse("") }

    #[test]
    fn test_minimal() { check_parse("()") }

    #[test]
    fn test_string() { check_parse("hello") }

    #[test]
    fn test_qstring() { check_parse("\"hello\"") }

    #[test]
    fn test_number() { check_parse("1.3") }

    #[test]
    fn test_float_vs_int() { check_parse("2.0") }

    #[test]
    fn test_double() { check_parse("(())") }

    #[test]
    fn test_br_string() { check_parse("(world)") }

    #[test]
    fn test_br_qstring() { check_parse("(\"world\")") }

    #[test]
    fn test_br_int() { check_parse("(42)") }

    #[test]
    fn test_br_float() { check_parse("(12.7)") }

    #[test]
    fn test_br_qbrstring() { check_parse("(\"(()\")") }

    #[test]
    #[should_panic(expected="Parse Error 1:1: end of document reached")]
    fn test_invalid1() { parse_fail("(") }

    #[test]
    #[should_panic(expected="Parse Error 1:0: unmatched )")]
    fn test_invalid2() { parse_fail(")") }

    #[test]
    #[should_panic(expected="Parse Error 1:6: end of document reached")]
    fn test_invalid3() { parse_fail("\"hello") }

    #[test]
    fn test_complex() { check_parse("(module SWITCH_3W_SIDE_MMP221-R (layer F.Cu) (descr \"\") (pad 1 thru_hole rect (size 1.2 1.2) (at -2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 2 thru_hole rect (size 1.2 1.2) (at 0.0 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 3 thru_hole rect (size 1.2 1.2) (at 2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 5 thru_hole rect (size 1.2 1.2) (at 0.0 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 6 thru_hole rect (size 1.2 1.2) (at -2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 4 thru_hole rect (size 1.2 1.2) (at 2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (fp_line (start -4.5 -1.75) (end 4.5 -1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 -1.75) (end 4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 1.75) (end -4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start -4.5 1.75) (end -4.5 -1.75) (layer F.SilkS) (width 0.127)))") }
}
<commit_msg>atom as string<commit_after>// (c) 2015 Joost Yervante Damad <joost@damad.be>

// loosely based on https://github.com/cgaebel/sexp
// latest version can be found at https://github.com/andete/rust_sexp

use std::fmt;
use std::str::FromStr;
use std::f64;

use std::fs::File;
use std::io::prelude::*;

pub enum Atom {
  S(String),
  Q(String),
  I(i64),
  F(f64),
}

impl Atom {
    pub fn f(&self) -> Result<f64,String> {
        match *self {
            Atom::F(f) => Ok(f),
            _ => Err(String::from("not a float"))
        }
    }
    pub fn i(&self) -> Result<i64,String> {
        match *self {
            Atom::I(i) => Ok(i),
            _ => Err(String::from("not an int"))
        }
    }
    pub fn string(&self) -> Result<String,String> {
        match *self {
            Atom::S(ref s) => Ok(s.clone()),
            Atom::Q(ref s) => Ok(s.clone()),
            _ => Err(String::from("not a string"))
        }
    }

}

pub enum Sexp {
  Atom(Atom),
  Empty,
  List(Vec<Sexp>),
}

impl Sexp {
    pub fn atom(&self) -> Result<&Atom,String> {
        match *self {
            Sexp::Atom(ref f) => Ok(f),
            _ => Err(String::from("not an atom"))
        }
    }
    pub fn list(&self) -> Result<&Vec<Sexp>,String> {
        match *self {
            Sexp::List(ref v) => Ok(v),
            _ => Err(String::from("not a list"))
        }
    }
}

pub struct ParseError {
    msg: &'static str,
    line: usize,
    lpos: usize,
}

pub enum Error {
    ParseError(ParseError),
    IOError(std::io::Error),
}

pub type ERes<T> = Result<T, Error>;

fn err_parse<T>(msg: &'static str, state: &ParseState) -> ERes<T> {
    Err(Error::ParseError(ParseError { msg: msg, line: state.line, lpos: state.lpos}))
}

fn err_io<T>(err: std::io::Error) -> ERes<T> {
    Err(Error::IOError(err))
}

struct ParseState {
    pos: usize,
    line: usize,
    lpos: usize,
    vec: Vec<char>,
    len: usize,
}

impl ParseState {
    fn peek(&self) -> ERes<char> {
        if self.pos >= self.len {
            return err_parse("end of document reached", self)
        }
        Ok(self.vec[self.pos])
    }
    fn peek_option(&self) -> Option<char> {
        if self.pos >= self.len {
            return None
        }
        Some(self.vec[self.pos])
    }
    fn next(&mut self) -> ERes<()> {
        if self.pos >= self.len {
            return err_parse("end of document reached", self)
        }
        let c = self.vec[self.pos];
        self.pos += 1;
        match c {
            '\r' | '\n' => {
                self.lpos = 0;
                self.line += 1;
            }
            _ => {
                self.lpos += 1;
            }
        }
        Ok(())
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::ParseError(ref p) =>
                write!(f, "Parse Error {}:{}: {}", p.line, p.lpos, p.msg),
            Error::IOError(ref i) =>
                write!(f, "{}", i)
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            Error::ParseError(ref p) =>
                write!(f, "Parse Error {}:{}: {}", p.line, p.lpos, p.msg),
            Error::IOError(ref i) =>
                write!(f, "{}", i)
        }
    }
}

impl fmt::Display for Atom {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    match *self {
        Atom::S(ref s) => write!(f, "{}", s),
        Atom::Q(ref s) => write!(f, "\"{}\"", s),
        Atom::I(i)     => write!(f, "{}", i),
        Atom::F(d)     => {
            let z = d.floor();
            if d - z < f64::EPSILON {
                write!(f, "{}.0", z)
            } else {
                write!(f, "{}", d)
            }
        }
    }
  }
}

impl fmt::Display for Sexp {
  fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
    match *self {
      Sexp::Atom(ref a) => write!(f, "{}", a),
      Sexp::List(ref xs) => {
        try!(write!(f, "("));
        for (i, x) in xs.iter().enumerate() {
          let s = if i == 0 { "" } else { " " };
          try!(write!(f, "{}{}", s, x));
        }
        write!(f, ")")
      },
      Sexp::Empty => write!(f, ""),
    }
  }
}

fn parse_list(state: &mut ParseState) -> ERes<Sexp> {
    //println!("list");
    try!(state.next()); // skip (
    let mut l: Vec<Sexp> = Vec::new();
    loop {
        match try!(state.peek()) {
            ')' => {
                try!(state.next());
                break;
            }
            _ => {
                l.push(try!(parse_sexp(state)));
            }
        }
    }
    //println!("Found list");
    Ok(Sexp::List(l))
}

fn parse_quoted_string(state: &mut ParseState) -> ERes<Atom> {
    //println!("qstring");
    try!(state.next()); // skip "
    let mut s = String::new();
    loop {
        match try!(state.peek()) {
            '"' => {
                try!(state.next());
                break
            }
            x @ '\r' | x @ '\n' => {
                s.push(x);
                try!(state.next());
                }
            x => {
                s.push(x);
                try!(state.next());
            }
        }
    }
    //println!("Found quoted string {}", s);
    Ok(Atom::Q(s))
}

fn parse_string(state: &mut ParseState) -> ERes<Atom> {
    //println!("string");
    let mut s = String::new();
    loop {
        match state.peek_option() {
            Some(x) => {
                match x {
                    ' ' | '\t' | '\r' | '\n' | ')' => break,
                    '"' => return err_parse("unexpected \" in string", state),
                    x => s.push(x),
                }
            }
            None => {
                break;
            }
        }
        try!(state.next())
    }
    //println!("Found string {}", s);
    Ok(Atom::S(s))
}

fn parse_number(state: &mut ParseState) -> ERes<Atom> {
    //println!("number");
    let mut s = String::new();
    loop {
        match state.peek_option() {
            Some(x) => {
                match x {
                    ' ' | '\r' | '\n' | '\t' | ')' => {
                        break
                    },
                    '0' ... '9' | '.' | '-' => {
                        s.push(state.vec[state.pos])
                    },
                    _ => {
                        return err_parse("unexpected char in number", state)
                    },
                }
            }
            None => {
                break
            }
        }
        try!(state.next())
    }
    //println!("Found number {}", s);
    let s2: &str = &s[..];
    if s.contains('.') {
        Ok(Atom::F(f64::from_str(s2).unwrap()))
    } else {
        Ok(Atom::I(i64::from_str(s2).unwrap()))
    }
}

fn parse_atom(state: &mut ParseState) -> ERes<Sexp> {
    //println!("atom");
    let a = match try!(state.peek()) {
        '"' => {
            try!(parse_quoted_string(state))
        }
        '0' ... '9' | '.' | '-' => {
            try!(parse_number(state))
        }
        _ => {
            try!(parse_string(state))
        }
    };
    Ok(Sexp::Atom(a))
}

fn parse_sexp(state: &mut ParseState) -> ERes<Sexp> {
    loop {
        match try!(state.peek()) {
            '(' => {
                return parse_list(state)
            }
            ' ' | '\t' | '\r' | '\n' => {
                try!(state.next());
            }
            ')' => {
                return err_parse("unmatched )", state)
            }
            _ => {
                return parse_atom(state)
            }
        }
    }
}

fn parse(data: &str) -> ERes<Sexp> {
    if data.len() == 0 {
        Ok(Sexp::Empty)
    } else {
        let vec: Vec<char> = data.chars().collect();
        let len = vec.len();
        let state = &mut ParseState { pos: 0, line: 1, lpos: 0, vec: vec, len: len };
        parse_sexp(state)
    }
}

fn read_file(name: &str) -> Result<String, std::io::Error> {
    let mut f = try!(File::open(name));
    let mut s = String::new();
    try!(f.read_to_string(&mut s));
    Ok(s)
}

pub fn parse_str(s: &str) -> ERes<Sexp> {
    parse(s)
}

pub fn parse_file(name: &str) -> ERes<Sexp> {
    let s = try!(match read_file(name) {
        Ok(s) => Ok(s),
        Err(x) => err_io(x),
    });
    parse(&s[..])
}

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

    #[allow(dead_code)]
    fn check_parse(s: &str) {
        let e = parse_str(s).unwrap();
        let t = format!("{}", e);
        assert_eq!(s, t);
    }

    #[allow(dead_code)]
    fn parse_fail(s: &str) {
        parse_str(s).unwrap();
    }

    #[test]
    fn test_empty() { check_parse("") }

    #[test]
    fn test_minimal() { check_parse("()") }

    #[test]
    fn test_string() { check_parse("hello") }

    #[test]
    fn test_qstring() { check_parse("\"hello\"") }

    #[test]
    fn test_number() { check_parse("1.3") }

    #[test]
    fn test_float_vs_int() { check_parse("2.0") }

    #[test]
    fn test_double() { check_parse("(())") }

    #[test]
    fn test_br_string() { check_parse("(world)") }

    #[test]
    fn test_br_qstring() { check_parse("(\"world\")") }

    #[test]
    fn test_br_int() { check_parse("(42)") }

    #[test]
    fn test_br_float() { check_parse("(12.7)") }

    #[test]
    fn test_br_qbrstring() { check_parse("(\"(()\")") }

    #[test]
    #[should_panic(expected="Parse Error 1:1: end of document reached")]
    fn test_invalid1() { parse_fail("(") }

    #[test]
    #[should_panic(expected="Parse Error 1:0: unmatched )")]
    fn test_invalid2() { parse_fail(")") }

    #[test]
    #[should_panic(expected="Parse Error 1:6: end of document reached")]
    fn test_invalid3() { parse_fail("\"hello") }

    #[test]
    fn test_complex() { check_parse("(module SWITCH_3W_SIDE_MMP221-R (layer F.Cu) (descr \"\") (pad 1 thru_hole rect (size 1.2 1.2) (at -2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 2 thru_hole rect (size 1.2 1.2) (at 0.0 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 3 thru_hole rect (size 1.2 1.2) (at 2.5 -1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 5 thru_hole rect (size 1.2 1.2) (at 0.0 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 6 thru_hole rect (size 1.2 1.2) (at -2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (pad 4 thru_hole rect (size 1.2 1.2) (at 2.5 1.6 0) (layers *.Cu *.Mask) (drill 0.8)) (fp_line (start -4.5 -1.75) (end 4.5 -1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 -1.75) (end 4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start 4.5 1.75) (end -4.5 1.75) (layer F.SilkS) (width 0.127)) (fp_line (start -4.5 1.75) (end -4.5 -1.75) (layer F.SilkS) (width 0.127)))") }
}
<|endoftext|>


<commit_before>#![forbid(unsafe_code)]
#[cfg(feature = "compression")]
#[cfg_attr(feature = "compression", doc(hidden))]
pub use include_flate::flate;

#[allow(unused_imports)]
#[macro_use]
extern crate rust_embed_impl;
pub use rust_embed_impl::*;

pub use rust_embed_utils::{EmbeddedFile, Metadata};

#[doc(hidden)]
pub extern crate rust_embed_utils as utils;

/// A directory of binary assets.
///
/// They should be embedded into the executable for release builds,
/// but can be read from the filesystem for debug builds.
///
/// This trait is meant to be derived like so:
/// ```
/// use rust_embed::RustEmbed;
///
/// #[derive(RustEmbed)]
/// #[folder = "examples/public/"]
/// struct Asset;
///
/// fn main() {}
/// ```

pub trait RustEmbed {
  /// Given a relative path from the assets folder, returns the bytes if found.
  ///
  /// If the feature `debug-embed` is enabled or the binary is compiled in
  /// release mode, the bytes have been embeded in the binary and a
  /// `Cow::Borrowed(&'static [u8])` is returned.
  ///
  /// Otherwise, the bytes are read from the file system on each call and a
  /// `Cow::Owned(Vec<u8>)` is returned.
  fn get(file_path: &str) -> Option<EmbeddedFile>;

  /// Iterates the files in this assets folder.
  ///
  /// If the feature `debug-embed` is enabled or the binary is compiled in
  /// release mode, a static array to the list of relative paths to the files
  /// is used.
  ///
  /// Otherwise, the files are listed from the file system on each call.
  fn iter() -> Filenames;
}

/// An iterator type over filenames.
///
/// This enum exists for optimization purposes, to avoid boxing the iterator in
/// some cases. Do not try and match on it, as different variants will exist
/// depending on the compilation context.
pub enum Filenames {
  /// Release builds use a nameable iterator type, which can be stack-allocated.
  #[cfg(any(not(debug_assertions), feature = "debug-embed"))]
  Embedded(std::slice::Iter<'static, &'static str>),

  /// The debug iterator type is currently unnamable and still needs to be
  /// boxed.
  #[cfg(all(debug_assertions, not(feature = "debug-embed")))]
  Dynamic(Box<dyn Iterator<Item = std::borrow::Cow<'static, str>>>),
}

impl Iterator for Filenames {
  type Item = std::borrow::Cow<'static, str>;
  fn next(&mut self) -> Option<Self::Item> {
    match self {
      #[cfg(any(not(debug_assertions), feature = "debug-embed"))]
      Filenames::Embedded(names) => names.next().map(|x| std::borrow::Cow::from(*x)),

      #[cfg(all(debug_assertions, not(feature = "debug-embed")))]
      Filenames::Dynamic(boxed) => boxed.next(),
    }
  }
}
<commit_msg>Update documentation with EmbeddedFile changes<commit_after>#![forbid(unsafe_code)]
#[cfg(feature = "compression")]
#[cfg_attr(feature = "compression", doc(hidden))]
pub use include_flate::flate;

#[allow(unused_imports)]
#[macro_use]
extern crate rust_embed_impl;
pub use rust_embed_impl::*;

pub use rust_embed_utils::{EmbeddedFile, Metadata};

#[doc(hidden)]
pub extern crate rust_embed_utils as utils;

/// A directory of binary assets.
///
/// The files in the specified folder will be embedded into the executable in
/// release builds. Debug builds will read the data from the file system at
/// runtime.
///
/// This trait is meant to be derived like so:
/// ```
/// use rust_embed::RustEmbed;
///
/// #[derive(RustEmbed)]
/// #[folder = "examples/public/"]
/// struct Asset;
///
/// fn main() {}
/// ```
pub trait RustEmbed {
  /// Get an embedded file and its metadata.
  ///
  /// If the feature `debug-embed` is enabled or the binary was compiled in
  /// release mode, the file information is embedded in the binary and the file
  /// data is returned as a `Cow::Borrowed(&'static [u8])`.
  ///
  /// Otherwise, the information is read from the file system on each call and
  /// the file data is returned as a `Cow::Owned(Vec<u8>)`.
  fn get(file_path: &str) -> Option<EmbeddedFile>;

  /// Iterates over the file paths in the folder.
  ///
  /// If the feature `debug-embed` is enabled or the binary is compiled in
  /// release mode, a static array containing the list of relative file paths
  /// is used.
  ///
  /// Otherwise, the files are listed from the file system on each call.
  fn iter() -> Filenames;
}

/// An iterator over filenames.
///
/// This enum exists for optimization purposes, to avoid boxing the iterator in
/// some cases. Do not try and match on it, as different variants will exist
/// depending on the compilation context.
pub enum Filenames {
  /// Release builds use a named iterator type, which can be stack-allocated.
  #[cfg(any(not(debug_assertions), feature = "debug-embed"))]
  Embedded(std::slice::Iter<'static, &'static str>),

  /// The debug iterator type is currently unnameable and still needs to be
  /// boxed.
  #[cfg(all(debug_assertions, not(feature = "debug-embed")))]
  Dynamic(Box<dyn Iterator<Item = std::borrow::Cow<'static, str>>>),
}

impl Iterator for Filenames {
  type Item = std::borrow::Cow<'static, str>;
  fn next(&mut self) -> Option<Self::Item> {
    match self {
      #[cfg(any(not(debug_assertions), feature = "debug-embed"))]
      Filenames::Embedded(names) => names.next().map(|x| std::borrow::Cow::from(*x)),

      #[cfg(all(debug_assertions, not(feature = "debug-embed")))]
      Filenames::Dynamic(boxed) => boxed.next(),
    }
  }
}
<|endoftext|>


<commit_before>//! Lettre is an email library that allows creating and sending messages. It provides:
//!
//! * An easy to use email builder
//! * Pluggable email transports
//! * Unicode support
//! * Secure defaults
//! * Async support
//!
//! Lettre requires Rust 1.56.0 or newer.
//!
//! ## Features
//!
//! This section lists each lettre feature and briefly explains it.
//! More info about each module can be found in the corresponding module page.
//!
//! Features with `📫` near them are enabled by default.
//!
//! ### Typed message builder
//!
//! _Strongly typed [`message`] builder_
//!
//! * **builder** 📫: Enable the [`Message`] builder
//! * **hostname** 📫: Try to use the actual system hostname in the `Message-ID` header
//!
//! ### SMTP transport
//!
//! _Send emails using [`SMTP`]_
//!
//! * **smtp-transport** 📫: Enable the SMTP transport
//! * **pool** 📫: Connection pool for SMTP transport
//! * **hostname** 📫: Try to use the actual system hostname for the SMTP `CLIENTID`
//!
//! #### SMTP over TLS via the native-tls crate
//!
//! _Secure SMTP connections using TLS from the `native-tls` crate_
//!
//! Uses schannel on Windows, Security-Framework on macOS, and OpenSSL on Linux.
//!
//! * **native-tls** 📫: TLS support for the synchronous version of the API
//! * **tokio1-native-tls**: TLS support for the `tokio1` async version of the API
//!
//! NOTE: native-tls isn't supported with `async-std`
//!
//! #### SMTP over TLS via the rustls crate
//!
//! _Secure SMTP connections using TLS from the `rustls-tls` crate_
//!
//! Rustls uses [ring] as the cryptography implementation. As a result, [not all Rust's targets are supported][ring-support].
//!
//! * **rustls-tls**: TLS support for the synchronous version of the API
//! * **tokio1-rustls-tls**: TLS support for the `tokio1` async version of the API
//! * **async-std1-rustls-tls**: TLS support for the `async-std1` async version of the API
//!
//! ### Sendmail transport
//!
//! _Send emails using the [`sendmail`] command_
//!
//! * **sendmail-transport**: Enable the `sendmail` transport
//!
//! ### File transport
//!
//! _Save emails as an `.eml` [`file`]_
//!
//! * **file-transport**: Enable the file transport (saves emails into an `.eml` file)
//! * **file-transport-envelope**: Allow writing the envelope into a JSON file (additionally saves envelopes into a `.json` file)
//!
//! ### Async execution runtimes
//!
//! _Use [tokio] or [async-std] as an async execution runtime for sending emails_
//!
//! The correct runtime version must be chosen in order for lettre to work correctly.
//! For example, when sending emails from a Tokio 1.x context, the Tokio 1.x executor
//! ([`Tokio1Executor`]) must be used. Using a different version (for example Tokio 0.2.x),
//! or async-std, would result in a runtime panic.
//!
//! * **tokio1**: Allow to asynchronously send emails using [Tokio 1.x]
//! * **async-std1**: Allow to asynchronously send emails using [async-std 1.x]
//!
//! NOTE: native-tls isn't supported with `async-std`
//!
//! ### Misc features
//!
//! _Additional features_
//!
//! * **serde**: Serialization/Deserialization of entities
//! * **tracing**: Logging using the `tracing` crate
//! * **mime03**: Allow creating a [`ContentType`] from an existing [mime 0.3] `Mime` struct
//! * **dkim**: Add support for signing email with DKIM
//!
//! [`SMTP`]: crate::transport::smtp
//! [`sendmail`]: crate::transport::sendmail
//! [`file`]: crate::transport::file
//! [`ContentType`]: crate::message::header::ContentType
//! [tokio]: https://docs.rs/tokio/1
//! [async-std]: https://docs.rs/async-std/1
//! [ring]: https://github.com/briansmith/ring#ring
//! [ring-support]: https://github.com/briansmith/ring#online-automated-testing
//! [Tokio 1.x]: https://docs.rs/tokio/1
//! [async-std 1.x]: https://docs.rs/async-std/1
//! [mime 0.3]: https://docs.rs/mime/0.3
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376

#![doc(html_root_url = "https://docs.rs/crate/lettre/0.10.0")]
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
#![forbid(unsafe_code)]
#![deny(
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unstable_features,
    unused_import_braces,
    rust_2018_idioms,
    clippy::string_add,
    clippy::string_add_assign,
    clippy::clone_on_ref_ptr,
    clippy::verbose_file_reads,
    clippy::unnecessary_self_imports,
    clippy::string_to_string,
    clippy::mem_forget,
    clippy::cast_lossless,
    clippy::inefficient_to_string,
    clippy::inline_always,
    clippy::linkedlist,
    clippy::macro_use_imports,
    clippy::manual_assert,
    clippy::unnecessary_join,
    clippy::wildcard_imports,
    clippy::zero_sized_map_values
)]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[cfg(not(lettre_ignore_tls_mismatch))]
mod compiletime_checks {
    #[cfg(all(feature = "native-tls", feature = "boring-tls"))]
    compile_error!("feature \"native-tls\" and feature \"boring-tls\" cannot be enabled at the same time, otherwise
    the executable will fail to link.");

    #[cfg(all(
        feature = "tokio1",
        feature = "native-tls",
        not(feature = "tokio1-native-tls")
    ))]
    compile_error!("Lettre is being built with the `tokio1` and the `native-tls` features, but the `tokio1-native-tls` feature hasn't been turned on.
    If you were trying to opt into `rustls-tls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
    Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");

    #[cfg(all(
        feature = "tokio1",
        feature = "rustls-tls",
        not(feature = "tokio1-rustls-tls")
    ))]
    compile_error!("Lettre is being built with the `tokio1` and the `rustls-tls` features, but the `tokio1-rustls-tls` feature hasn't been turned on.
    If you'd like to use `native-tls` make sure that the `rustls-tls` feature hasn't been enabled by mistake.
    Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");

    #[cfg(all(
        feature = "tokio1",
        feature = "boring-tls",
        not(feature = "tokio1-boring-tls")
    ))]
    compile_error!("Lettre is being built with the `tokio1` and the `boring-tls` features, but the `tokio1-boring-tls` feature hasn't been turned on.
    If you'd like to use `boring-tls` make sure that the `rustls-tls` feature hasn't been enabled by mistake.
    Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");

    /*
    #[cfg(all(
        feature = "async-std1",
        feature = "native-tls",
        not(feature = "async-std1-native-tls")
    ))]
    compile_error!("Lettre is being built with the `async-std1` and the `native-tls` features, but the `async-std1-native-tls` feature hasn't been turned on.
    If you'd like to use rustls make sure that the `native-tls` hasn't been enabled by mistake (you may need to import lettre without default features)
    If you're building a library which depends on lettre import it without default features and enable just the features you need.");
    */
    #[cfg(all(
        feature = "async-std1",
        feature = "native-tls",
        not(feature = "async-std1-native-tls")
    ))]
    compile_error!("Lettre is being built with the `async-std1` and the `native-tls` features, but the async-std integration doesn't support native-tls yet.
If you'd like to work on the issue please take a look at https://github.com/lettre/lettre/issues/576.
If you were trying to opt into `rustls-tls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");

    #[cfg(all(
        feature = "async-std1",
        feature = "rustls-tls",
        not(feature = "async-std1-rustls-tls")
    ))]
    compile_error!("Lettre is being built with the `async-std1` and the `rustls-tls` features, but the `async-std1-rustls-tls` feature hasn't been turned on.
If you'd like to use `native-tls` make sure that the `rustls-tls` hasn't been enabled by mistake.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
}

pub mod address;
pub mod error;
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
mod executor;
#[cfg(feature = "builder")]
#[cfg_attr(docsrs, doc(cfg(feature = "builder")))]
pub mod message;
pub mod transport;

use std::error::Error as StdError;

#[cfg(feature = "async-std1")]
pub use self::executor::AsyncStd1Executor;
#[cfg(all(any(feature = "tokio1", feature = "async-std1")))]
pub use self::executor::Executor;
#[cfg(feature = "tokio1")]
pub use self::executor::Tokio1Executor;
#[cfg(all(any(feature = "tokio1", feature = "async-std1")))]
#[doc(inline)]
pub use self::transport::AsyncTransport;
pub use crate::address::Address;
#[cfg(feature = "builder")]
#[doc(inline)]
pub use crate::message::Message;
#[cfg(all(
    feature = "file-transport",
    any(feature = "tokio1", feature = "async-std1")
))]
#[doc(inline)]
pub use crate::transport::file::AsyncFileTransport;
#[cfg(feature = "file-transport")]
#[doc(inline)]
pub use crate::transport::file::FileTransport;
#[cfg(all(
    feature = "sendmail-transport",
    any(feature = "tokio1", feature = "async-std1")
))]
#[doc(inline)]
pub use crate::transport::sendmail::AsyncSendmailTransport;
#[cfg(feature = "sendmail-transport")]
#[doc(inline)]
pub use crate::transport::sendmail::SendmailTransport;
#[cfg(all(
    feature = "smtp-transport",
    any(feature = "tokio1", feature = "async-std1")
))]
pub use crate::transport::smtp::AsyncSmtpTransport;
#[cfg(feature = "smtp-transport")]
pub use crate::transport::smtp::SmtpTransport;
#[doc(inline)]
pub use crate::transport::Transport;
use crate::{address::Envelope, error::Error};

pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;

#[cfg(test)]
#[cfg(feature = "builder")]
mod test {
    use super::*;
    use crate::message::{header, header::Headers, Mailbox, Mailboxes};

    #[test]
    fn envelope_from_headers() {
        let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
        let to = Mailboxes::new().with("amousset@example.com".parse().unwrap());

        let mut headers = Headers::new();
        headers.set(header::From(from));
        headers.set(header::To(to));

        assert_eq!(
            Envelope::try_from(&headers).unwrap(),
            Envelope::new(
                Some(Address::new("kayo", "example.com").unwrap()),
                vec![Address::new("amousset", "example.com").unwrap()]
            )
            .unwrap()
        );
    }

    #[test]
    fn envelope_from_headers_sender() {
        let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
        let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap());
        let to = Mailboxes::new().with("amousset@example.com".parse().unwrap());

        let mut headers = Headers::new();
        headers.set(header::From::from(from));
        headers.set(header::Sender::from(sender));
        headers.set(header::To::from(to));

        assert_eq!(
            Envelope::try_from(&headers).unwrap(),
            Envelope::new(
                Some(Address::new("kayo2", "example.com").unwrap()),
                vec![Address::new("amousset", "example.com").unwrap()]
            )
            .unwrap()
        );
    }

    #[test]
    fn envelope_from_headers_no_to() {
        let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
        let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap());

        let mut headers = Headers::new();
        headers.set(header::From::from(from));
        headers.set(header::Sender::from(sender));

        assert!(Envelope::try_from(&headers).is_err(),);
    }
}
<commit_msg>Document the boring-tls features in lib.rs (#798)<commit_after>//! Lettre is an email library that allows creating and sending messages. It provides:
//!
//! * An easy to use email builder
//! * Pluggable email transports
//! * Unicode support
//! * Secure defaults
//! * Async support
//!
//! Lettre requires Rust 1.56.0 or newer.
//!
//! ## Features
//!
//! This section lists each lettre feature and briefly explains it.
//! More info about each module can be found in the corresponding module page.
//!
//! Features with `📫` near them are enabled by default.
//!
//! ### Typed message builder
//!
//! _Strongly typed [`message`] builder_
//!
//! * **builder** 📫: Enable the [`Message`] builder
//! * **hostname** 📫: Try to use the actual system hostname in the `Message-ID` header
//!
//! ### SMTP transport
//!
//! _Send emails using [`SMTP`]_
//!
//! * **smtp-transport** 📫: Enable the SMTP transport
//! * **pool** 📫: Connection pool for SMTP transport
//! * **hostname** 📫: Try to use the actual system hostname for the SMTP `CLIENTID`
//!
//! #### SMTP over TLS via the native-tls crate
//!
//! _Secure SMTP connections using TLS from the `native-tls` crate_
//!
//! Uses schannel on Windows, Security-Framework on macOS, and OpenSSL on Linux.
//!
//! * **native-tls** 📫: TLS support for the synchronous version of the API
//! * **tokio1-native-tls**: TLS support for the `tokio1` async version of the API
//!
//! NOTE: native-tls isn't supported with `async-std`
//!
//! #### SMTP over TLS via the boring crate (Boring TLS)
//!
//! _Secure SMTP connections using TLS from the `boring-tls` crate_
//!
//! * **boring-tls**: TLS support for the synchronous version of the API
//! * **tokio1-boring-tls**: TLS support for the `tokio1` async version of the API
//!
//! NOTE: boring-tls isn't supported with `async-std`
//!
//! #### SMTP over TLS via the rustls crate
//!
//! _Secure SMTP connections using TLS from the `rustls-tls` crate_
//!
//! Rustls uses [ring] as the cryptography implementation. As a result, [not all Rust's targets are supported][ring-support].
//!
//! * **rustls-tls**: TLS support for the synchronous version of the API
//! * **tokio1-rustls-tls**: TLS support for the `tokio1` async version of the API
//! * **async-std1-rustls-tls**: TLS support for the `async-std1` async version of the API
//!
//! ### Sendmail transport
//!
//! _Send emails using the [`sendmail`] command_
//!
//! * **sendmail-transport**: Enable the `sendmail` transport
//!
//! ### File transport
//!
//! _Save emails as an `.eml` [`file`]_
//!
//! * **file-transport**: Enable the file transport (saves emails into an `.eml` file)
//! * **file-transport-envelope**: Allow writing the envelope into a JSON file (additionally saves envelopes into a `.json` file)
//!
//! ### Async execution runtimes
//!
//! _Use [tokio] or [async-std] as an async execution runtime for sending emails_
//!
//! The correct runtime version must be chosen in order for lettre to work correctly.
//! For example, when sending emails from a Tokio 1.x context, the Tokio 1.x executor
//! ([`Tokio1Executor`]) must be used. Using a different version (for example Tokio 0.2.x),
//! or async-std, would result in a runtime panic.
//!
//! * **tokio1**: Allow to asynchronously send emails using [Tokio 1.x]
//! * **async-std1**: Allow to asynchronously send emails using [async-std 1.x]
//!
//! NOTE: native-tls isn't supported with `async-std`
//!
//! ### Misc features
//!
//! _Additional features_
//!
//! * **serde**: Serialization/Deserialization of entities
//! * **tracing**: Logging using the `tracing` crate
//! * **mime03**: Allow creating a [`ContentType`] from an existing [mime 0.3] `Mime` struct
//! * **dkim**: Add support for signing email with DKIM
//!
//! [`SMTP`]: crate::transport::smtp
//! [`sendmail`]: crate::transport::sendmail
//! [`file`]: crate::transport::file
//! [`ContentType`]: crate::message::header::ContentType
//! [tokio]: https://docs.rs/tokio/1
//! [async-std]: https://docs.rs/async-std/1
//! [ring]: https://github.com/briansmith/ring#ring
//! [ring-support]: https://github.com/briansmith/ring#online-automated-testing
//! [Tokio 1.x]: https://docs.rs/tokio/1
//! [async-std 1.x]: https://docs.rs/async-std/1
//! [mime 0.3]: https://docs.rs/mime/0.3
//! [DKIM]: https://datatracker.ietf.org/doc/html/rfc6376

#![doc(html_root_url = "https://docs.rs/crate/lettre/0.10.0")]
#![doc(html_favicon_url = "https://lettre.rs/favicon.ico")]
#![doc(html_logo_url = "https://avatars0.githubusercontent.com/u/15113230?v=4")]
#![forbid(unsafe_code)]
#![deny(
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unstable_features,
    unused_import_braces,
    rust_2018_idioms,
    clippy::string_add,
    clippy::string_add_assign,
    clippy::clone_on_ref_ptr,
    clippy::verbose_file_reads,
    clippy::unnecessary_self_imports,
    clippy::string_to_string,
    clippy::mem_forget,
    clippy::cast_lossless,
    clippy::inefficient_to_string,
    clippy::inline_always,
    clippy::linkedlist,
    clippy::macro_use_imports,
    clippy::manual_assert,
    clippy::unnecessary_join,
    clippy::wildcard_imports,
    clippy::zero_sized_map_values
)]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[cfg(not(lettre_ignore_tls_mismatch))]
mod compiletime_checks {
    #[cfg(all(feature = "native-tls", feature = "boring-tls"))]
    compile_error!("feature \"native-tls\" and feature \"boring-tls\" cannot be enabled at the same time, otherwise
    the executable will fail to link.");

    #[cfg(all(
        feature = "tokio1",
        feature = "native-tls",
        not(feature = "tokio1-native-tls")
    ))]
    compile_error!("Lettre is being built with the `tokio1` and the `native-tls` features, but the `tokio1-native-tls` feature hasn't been turned on.
    If you were trying to opt into `rustls-tls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
    Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");

    #[cfg(all(
        feature = "tokio1",
        feature = "rustls-tls",
        not(feature = "tokio1-rustls-tls")
    ))]
    compile_error!("Lettre is being built with the `tokio1` and the `rustls-tls` features, but the `tokio1-rustls-tls` feature hasn't been turned on.
    If you'd like to use `native-tls` make sure that the `rustls-tls` feature hasn't been enabled by mistake.
    Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");

    #[cfg(all(
        feature = "tokio1",
        feature = "boring-tls",
        not(feature = "tokio1-boring-tls")
    ))]
    compile_error!("Lettre is being built with the `tokio1` and the `boring-tls` features, but the `tokio1-boring-tls` feature hasn't been turned on.
    If you'd like to use `boring-tls` make sure that the `rustls-tls` feature hasn't been enabled by mistake.
    Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");

    /*
    #[cfg(all(
        feature = "async-std1",
        feature = "native-tls",
        not(feature = "async-std1-native-tls")
    ))]
    compile_error!("Lettre is being built with the `async-std1` and the `native-tls` features, but the `async-std1-native-tls` feature hasn't been turned on.
    If you'd like to use rustls make sure that the `native-tls` hasn't been enabled by mistake (you may need to import lettre without default features)
    If you're building a library which depends on lettre import it without default features and enable just the features you need.");
    */
    #[cfg(all(
        feature = "async-std1",
        feature = "native-tls",
        not(feature = "async-std1-native-tls")
    ))]
    compile_error!("Lettre is being built with the `async-std1` and the `native-tls` features, but the async-std integration doesn't support native-tls yet.
If you'd like to work on the issue please take a look at https://github.com/lettre/lettre/issues/576.
If you were trying to opt into `rustls-tls` and did not activate `native-tls`, disable the default-features of lettre in `Cargo.toml` and manually add the required features.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");

    #[cfg(all(
        feature = "async-std1",
        feature = "rustls-tls",
        not(feature = "async-std1-rustls-tls")
    ))]
    compile_error!("Lettre is being built with the `async-std1` and the `rustls-tls` features, but the `async-std1-rustls-tls` feature hasn't been turned on.
If you'd like to use `native-tls` make sure that the `rustls-tls` hasn't been enabled by mistake.
Make sure to apply the same to any of your crate dependencies that use the `lettre` crate.");
}

pub mod address;
pub mod error;
#[cfg(any(feature = "tokio1", feature = "async-std1"))]
mod executor;
#[cfg(feature = "builder")]
#[cfg_attr(docsrs, doc(cfg(feature = "builder")))]
pub mod message;
pub mod transport;

use std::error::Error as StdError;

#[cfg(feature = "async-std1")]
pub use self::executor::AsyncStd1Executor;
#[cfg(all(any(feature = "tokio1", feature = "async-std1")))]
pub use self::executor::Executor;
#[cfg(feature = "tokio1")]
pub use self::executor::Tokio1Executor;
#[cfg(all(any(feature = "tokio1", feature = "async-std1")))]
#[doc(inline)]
pub use self::transport::AsyncTransport;
pub use crate::address::Address;
#[cfg(feature = "builder")]
#[doc(inline)]
pub use crate::message::Message;
#[cfg(all(
    feature = "file-transport",
    any(feature = "tokio1", feature = "async-std1")
))]
#[doc(inline)]
pub use crate::transport::file::AsyncFileTransport;
#[cfg(feature = "file-transport")]
#[doc(inline)]
pub use crate::transport::file::FileTransport;
#[cfg(all(
    feature = "sendmail-transport",
    any(feature = "tokio1", feature = "async-std1")
))]
#[doc(inline)]
pub use crate::transport::sendmail::AsyncSendmailTransport;
#[cfg(feature = "sendmail-transport")]
#[doc(inline)]
pub use crate::transport::sendmail::SendmailTransport;
#[cfg(all(
    feature = "smtp-transport",
    any(feature = "tokio1", feature = "async-std1")
))]
pub use crate::transport::smtp::AsyncSmtpTransport;
#[cfg(feature = "smtp-transport")]
pub use crate::transport::smtp::SmtpTransport;
#[doc(inline)]
pub use crate::transport::Transport;
use crate::{address::Envelope, error::Error};

pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;

#[cfg(test)]
#[cfg(feature = "builder")]
mod test {
    use super::*;
    use crate::message::{header, header::Headers, Mailbox, Mailboxes};

    #[test]
    fn envelope_from_headers() {
        let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
        let to = Mailboxes::new().with("amousset@example.com".parse().unwrap());

        let mut headers = Headers::new();
        headers.set(header::From(from));
        headers.set(header::To(to));

        assert_eq!(
            Envelope::try_from(&headers).unwrap(),
            Envelope::new(
                Some(Address::new("kayo", "example.com").unwrap()),
                vec![Address::new("amousset", "example.com").unwrap()]
            )
            .unwrap()
        );
    }

    #[test]
    fn envelope_from_headers_sender() {
        let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
        let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap());
        let to = Mailboxes::new().with("amousset@example.com".parse().unwrap());

        let mut headers = Headers::new();
        headers.set(header::From::from(from));
        headers.set(header::Sender::from(sender));
        headers.set(header::To::from(to));

        assert_eq!(
            Envelope::try_from(&headers).unwrap(),
            Envelope::new(
                Some(Address::new("kayo2", "example.com").unwrap()),
                vec![Address::new("amousset", "example.com").unwrap()]
            )
            .unwrap()
        );
    }

    #[test]
    fn envelope_from_headers_no_to() {
        let from = Mailboxes::new().with("kayo@example.com".parse().unwrap());
        let sender = Mailbox::new(None, "kayo2@example.com".parse().unwrap());

        let mut headers = Headers::new();
        headers.set(header::From::from(from));
        headers.set(header::Sender::from(sender));

        assert!(Envelope::try_from(&headers).is_err(),);
    }
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]

//! Implementation of a Micro Transport Protocol library,
//! as well as a small client and server.
//!
//! http://www.bittorrent.org/beps/bep_0029.html

extern crate utp;
use std::io::net::ip::{Ipv4Addr, SocketAddr};

fn usage() {
    println!("Usage: utp [-s|-c] <address> <port>");
}

fn main() {
    use utp::{UtpStream, UtpSocket};
    use std::from_str::FromStr;

    // Defaults
    static BUF_SIZE: uint = 4096;
    let mut addr = SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 8080 };

    let args = std::os::args();

    if args.len() == 4 {
        addr = SocketAddr {
            ip:   FromStr::from_str(args.get(2).as_slice()).unwrap(),
            port: FromStr::from_str(args.get(3).as_slice()).unwrap(),
        };
    }

    match args.get(1).as_slice() {
        "-s" => {
            let mut buf = [0, ..BUF_SIZE];
            let mut sock = UtpSocket::bind(addr).unwrap();
            println!("Serving on {}", addr);

            loop {
                match sock.recvfrom(buf) {
                    Ok((_read, _src)) => {}
                    Err(e) => fail!("{}", e),
                }
            }
        }
        "-c" => {
            let mut stream = UtpStream::connect(addr);

            let buf = [0xF, ..BUF_SIZE];
            match stream.write(buf) {
                Ok(_) => {},
                Err(e) => fail!("{}", e),
            }

            let mut buf = [0u8, ..BUF_SIZE];
            match stream.read(buf) {
                Ok(n) => println!("{}", Vec::from_slice(buf.slice(0, n))),
                Err(e) => println!("{}", e),
            }

            match stream.close() {
                Ok(_) => {},
                Err(e) => fail!("{}", e),
            }
            drop(stream);
        }
        _ => usage(),
    }
}
<commit_msg>Print usage if an incorrect number of arguments is used.<commit_after>#![feature(macro_rules)]

//! Implementation of a Micro Transport Protocol library,
//! as well as a small client and server.
//!
//! http://www.bittorrent.org/beps/bep_0029.html

extern crate utp;
use std::io::net::ip::{Ipv4Addr, SocketAddr};

fn usage() {
    println!("Usage: utp [-s|-c] <address> <port>");
}

fn main() {
    use utp::{UtpStream, UtpSocket};
    use std::from_str::FromStr;

    // Defaults
    static BUF_SIZE: uint = 4096;
    let mut addr = SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 8080 };

    let args = std::os::args();

	if args.len() != 2 && args.len() != 4 {
		usage();
		return;
	}

    if args.len() == 4 {
        addr = SocketAddr {
            ip:   FromStr::from_str(args.get(2).as_slice()).unwrap(),
            port: FromStr::from_str(args.get(3).as_slice()).unwrap(),
        };
    }

    match args.get(1).as_slice() {
        "-s" => {
            let mut buf = [0, ..BUF_SIZE];
            let mut sock = UtpSocket::bind(addr).unwrap();
            println!("Serving on {}", addr);

            loop {
                match sock.recvfrom(buf) {
                    Ok((_read, _src)) => {}
                    Err(e) => fail!("{}", e),
                }
            }
        }
        "-c" => {
            let mut stream = UtpStream::connect(addr);

            let buf = [0xF, ..BUF_SIZE];
            match stream.write(buf) {
                Ok(_) => {},
                Err(e) => fail!("{}", e),
            }

            let mut buf = [0u8, ..BUF_SIZE];
            match stream.read(buf) {
                Ok(n) => println!("{}", Vec::from_slice(buf.slice(0, n))),
                Err(e) => println!("{}", e),
            }

            match stream.close() {
                Ok(_) => {},
                Err(e) => fail!("{}", e),
            }
            drop(stream);
        }
        _ => usage(),
    }
}
<|endoftext|>


<commit_before>// Streambuffer -- Fast asynchronous file reading
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! Streambuffer, a library for fast asynchronous file reading.
//!
//! # Examples
//!
//! Map a file into memory and access it as an array of bytes. This is simple and will generally
//! outperform `Read::read_to_end()`, but it will block upon first access.
//!
//! ```
//! use streambuffer::StreamBuffer;
//! let fstream = StreamBuffer::open("src/lib.rs").unwrap();
//! let buffer = fstream.as_slice();
//! assert_eq!(buffer[3..49], b"Streambuffer -- Fast asynchronous file reading"[..]);
//! ```
//!
//! TODO: More and better (non-blocking) examples.

#![warn(missing_docs)]

use std::cmp;
use std::io;
use std::fs;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::slice;
use std::thread;

extern crate libc;

/// A memory-mapped file.
pub struct StreamBuffer {
    page_size: usize,
    buffer: *const u8,
    length: usize,
}

fn map_file(file: &fs::File) -> io::Result<(*const u8, usize)> {
    let fd = file.as_raw_fd();
    let length = try!(file.metadata()).len();

    if length > usize::max_value() as u64 {
        return Err(io::Error::new(io::ErrorKind::Other, "file is larger than address space"));
    }

    if length == 0 {
        return Err(io::Error::new(io::ErrorKind::Other, "file has size zero"));
    }

    let null = 0 as *mut libc::c_void;
    let result = unsafe {
        libc::mmap(null, length as usize, libc::PROT_READ, libc::MAP_PRIVATE, fd, 0)
    };

    if result == libc::MAP_FAILED {
        Err(io::Error::last_os_error())
    } else {
        Ok((result as *const u8, length as usize))
    }
}

fn unmap_file(buffer: *const u8, length: usize) {
    let result = unsafe { libc::munmap(buffer as *mut libc::c_void, length) };

    // `munmap` only fails due to incorrect usage, which is a program error, not a runtime failure.
    assert!(result == 0);
}

/// Writes whether the pages in the range starting at `buffer` with a length of `length` bytes
/// are resident in physical memory into `residency`. The size of `residency` must be at least
/// `length / page_size`. Both `buffer` and `length` must be a multiple of the page size.
fn get_resident(buffer: *const u8, length: usize, residency: &mut [bool]) {
    let result = unsafe {
        let residency_uchar = residency.as_mut_ptr() as *mut libc::c_uchar;
        libc::mincore(buffer as *mut libc::c_void, length, residency_uchar)
    };

    // Any error code except EAGAIN indicates a programming error.
    assert!(result == libc::EAGAIN || result == 0);

    // In the rare occasion that the kernel is busy, yield so we don't spam the kernel with
    // `mincore` calls, then try again.
    if result == libc::EAGAIN {
        thread::yield_now();
        get_resident(buffer, length, residency)
    }
}

/// Requests the kernel to make the specified range of bytes resident in physical memory. `buffer`
/// must be page-aligned.
fn prefetch(buffer: *const u8, length: usize) {
    let result = unsafe {
        libc::posix_madvise(buffer as *mut libc::c_void, length, libc::POSIX_MADV_WILLNEED)
    };

    // Any returned error code indicates a programming error, not a runtime error.
    assert_eq!(0, result);
}

fn get_page_size() -> usize {
    let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };

    // Assert that the page size is a power of two, which is assumed when the page size is used.
    assert!(page_size != 0);
    assert_eq!(0, page_size & (page_size - 1));

    page_size
}

/// Rounds `size` up to the nearest multiple of `power_of_two`.
fn round_up_to(size: usize, power_of_two: usize) -> usize {
    (size + (power_of_two - 1)) & !(power_of_two - 1)
}

#[test]
fn verify_round_up_to() {
    assert_eq!(1024, round_up_to(23, 1024));
    assert_eq!(1024, round_up_to(1024, 1024));
    assert_eq!(2048, round_up_to(1025, 1024));
}

/// Rounds `size` down to the nearest multiple of `power_of_two`.
fn round_down_to(size: usize, power_of_two: usize) -> usize {
    size & !(power_of_two - 1)
}

#[test]
fn verify_round_down_to() {
    assert_eq!(0, round_down_to(23, 1024));
    assert_eq!(1024, round_down_to(1024, 1024));
    assert_eq!(1024, round_down_to(1025, 1024));
}

impl StreamBuffer {
    /// Maps the file at `path` into memory.
    ///
    /// TODO: Document what happens when the file is changed after opening.
    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<StreamBuffer> {
        // Open the `fs::File` so we get all of std's error handling for free, then use it to
        // extract the file descriptor. The file is closed again when it goes out of scope, but
        // `mmap` only requires the descriptor to be open for the `mmap` call, so this is fine.
        let file = try!(fs::File::open(path));
        let (buffer, length) = try!(map_file(&file));
        let fstream = StreamBuffer {
            page_size: get_page_size(),
            buffer: buffer,
            length: length,
        };
        Ok(fstream)
    }

    /// Returns the length of the mapped file in bytes.
    pub fn len(&self) -> usize {
        self.length
    }

    /// Returns the number of bytes resident in physical memory, starting from `offset`.
    ///
    /// The slice `[offset..offset + resident_len]` can be accessed without causing page faults or
    /// disk access. Note that this is only a snapshot, and the kernel might decide to evict pages
    /// or make them resident at any time.
    ///
    /// The returned resident length is at most `length`.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    pub fn resident_len(&self, offset: usize, length: usize) -> usize {
        // The specified offset and length must lie within the buffer.
        assert!(offset + length <= self.length);

        let aligned_offset = round_down_to(offset, self.page_size);
        let aligned_length = round_up_to(length + (offset - aligned_offset), self.page_size);
        let num_pages = aligned_length / self.page_size;

        // There is a tradeoff here: to store residency information, we need an array of booleans.
        // The requested range can potentially be very large and it is only known at runtime. We
        // could allocate a vector here, but that requires a heap allocation just to get residency
        // information (which might in turn cause a page fault). Instead, check at most 32 pages at
        // once. This means more syscalls for large ranges, but it saves us the heap allocation,
        // and for ranges up to 32 pages (128 KiB typically) there is only one syscall.
        let mut residency = [false; 32];
        let mut pages_checked = 0;
        let mut pages_resident = 0;

        while pages_checked < num_pages {
            let pages_to_check = cmp::min(32, num_pages - pages_checked);
            let check_offset = (aligned_offset + pages_checked * self.page_size) as isize;
            let check_buffer = unsafe { self.buffer.offset(check_offset) };
            let check_length = pages_to_check * self.page_size;
            get_resident(check_buffer, check_length, &mut residency);

            // Count the number of resident pages.
            match residency[..pages_to_check].iter().position(|resident| !resident) {
                Some(non_resident) => {
                    // The index of the non-resident page is the number of resident pages.
                    pages_resident += non_resident;
                    break;
                }
                None => {
                    pages_resident += pages_to_check;
                    pages_checked += pages_to_check;
                }
            }
        }

        let resident_length = pages_resident * self.page_size + aligned_offset - offset;

        // Never return more than the requested length. The resident length might be larger than
        // the length of the buffer, because it is rounded up to the page size.
        cmp::min(length, resident_length)
    }

    /// Returns the file contents as a slice.
    ///
    /// Accessing elements of the slice might cause a page fault, blocking until the data has been
    /// read from disk.
    ///
    /// To avoid blocking, call `prefetch()` and check whether the memory is resident with
    /// `resident_len()`. The `try_slice()` method is similar to `as_slice()`, but it returns
    /// `None` if accessing the slice would block.
    pub fn as_slice(&self) -> &[u8] {
        unsafe { slice::from_raw_parts(self.buffer, self.length) }
    }

    /// Returns a slice if the requested range is resident in physical memory.
    ///
    /// If the slice is not resident, `prefetch()` is called, so that if the same slice is
    /// requested after a while, it likely is resident.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    pub fn try_slice(&self, offset: usize, length: usize) -> Option<&[u8]> {
        // The bounds check assertion is done in `resident_len()`, no need to duplicate it here.
        if self.resident_len(offset, length) < length {
            self.prefetch(offset, length);
            None
        } else {
            Some(&self.as_slice()[offset..offset + length])
        }
    }

    /// Advises the kernel to make a slice of the file resident in physical memory.
    ///
    /// This method does not block, meaning that when the function returns, the slice is not
    /// necessarily resident. After this function returns, the kernel may read the requested slice
    /// from disk and make it resident. Note that this is only an advice, the kernel need not honor
    /// it.
    ///
    /// To check whether the slice is resident at a later time, use `resident_len()`.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    pub fn prefetch(&self, offset: usize, length: usize) {
        // The specified offset and length must lie within the buffer.
        assert!(offset + length <= self.length);

        let aligned_offset = round_down_to(offset, self.page_size);
        let aligned_length = round_up_to(length + (offset - aligned_offset), self.page_size);

        let buffer = unsafe { self.buffer.offset(aligned_offset as isize) };
        prefetch(buffer, aligned_length);
    }
}

impl Drop for StreamBuffer {
    fn drop(&mut self) {
        unmap_file(self.buffer, self.length);
    }
}

#[test]
fn open_file() {
    let fstream = StreamBuffer::open("src/lib.rs");
    assert!(fstream.is_ok());
}

#[test]
fn make_resident() {
    let fstream = StreamBuffer::open("src/lib.rs").unwrap();

    // Touch the first page to make it resident.
    assert_eq!(fstream.as_slice()[3..15], b"Streambuffer"[..]);

    // Now at least that part should be resident.
    assert_eq!(fstream.resident_len(3, 12), 12);

    // If it is resident, `try_slice` should return a slice.
    assert_eq!(fstream.try_slice(3, 12), Some(&b"Streambuffer"[..]));
}

#[test]
fn prefetch_is_not_harmful() {
    let fstream = StreamBuffer::open("src/lib.rs").unwrap();

    // It is impossible to test that this actually works without root access to instruct the kernel
    // to drop its caches, but at least we can verify that calling `prefetch` is not harmful.
    fstream.prefetch(0, fstream.len());

    // Reading from the file should still work as normal.
    assert_eq!(fstream.as_slice()[3..15], b"Streambuffer"[..]);
}
<commit_msg>Add chunk_len_hint() method<commit_after>// Streambuffer -- Fast asynchronous file reading
// Copyright 2016 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.

//! Streambuffer, a library for fast asynchronous file reading.
//!
//! # Examples
//!
//! Map a file into memory and access it as an array of bytes. This is simple and will generally
//! outperform `Read::read_to_end()`, but it will block upon first access.
//!
//! ```
//! use streambuffer::StreamBuffer;
//! let fstream = StreamBuffer::open("src/lib.rs").unwrap();
//! let buffer = fstream.as_slice();
//! assert_eq!(buffer[3..49], b"Streambuffer -- Fast asynchronous file reading"[..]);
//! ```
//!
//! TODO: More and better (non-blocking) examples.

#![warn(missing_docs)]

use std::cmp;
use std::io;
use std::fs;
use std::os::unix::io::AsRawFd;
use std::path::Path;
use std::slice;
use std::thread;

extern crate libc;

/// A memory-mapped file.
pub struct StreamBuffer {
    page_size: usize,
    buffer: *const u8,
    length: usize,
}

fn map_file(file: &fs::File) -> io::Result<(*const u8, usize)> {
    let fd = file.as_raw_fd();
    let length = try!(file.metadata()).len();

    if length > usize::max_value() as u64 {
        return Err(io::Error::new(io::ErrorKind::Other, "file is larger than address space"));
    }

    if length == 0 {
        return Err(io::Error::new(io::ErrorKind::Other, "file has size zero"));
    }

    let null = 0 as *mut libc::c_void;
    let result = unsafe {
        libc::mmap(null, length as usize, libc::PROT_READ, libc::MAP_PRIVATE, fd, 0)
    };

    if result == libc::MAP_FAILED {
        Err(io::Error::last_os_error())
    } else {
        Ok((result as *const u8, length as usize))
    }
}

fn unmap_file(buffer: *const u8, length: usize) {
    let result = unsafe { libc::munmap(buffer as *mut libc::c_void, length) };

    // `munmap` only fails due to incorrect usage, which is a program error, not a runtime failure.
    assert!(result == 0);
}

/// Writes whether the pages in the range starting at `buffer` with a length of `length` bytes
/// are resident in physical memory into `residency`. The size of `residency` must be at least
/// `length / page_size`. Both `buffer` and `length` must be a multiple of the page size.
fn get_resident(buffer: *const u8, length: usize, residency: &mut [bool]) {
    let result = unsafe {
        let residency_uchar = residency.as_mut_ptr() as *mut libc::c_uchar;
        libc::mincore(buffer as *mut libc::c_void, length, residency_uchar)
    };

    // Any error code except EAGAIN indicates a programming error.
    assert!(result == libc::EAGAIN || result == 0);

    // In the rare occasion that the kernel is busy, yield so we don't spam the kernel with
    // `mincore` calls, then try again.
    if result == libc::EAGAIN {
        thread::yield_now();
        get_resident(buffer, length, residency)
    }
}

/// Requests the kernel to make the specified range of bytes resident in physical memory. `buffer`
/// must be page-aligned.
fn prefetch(buffer: *const u8, length: usize) {
    let result = unsafe {
        libc::posix_madvise(buffer as *mut libc::c_void, length, libc::POSIX_MADV_WILLNEED)
    };

    // Any returned error code indicates a programming error, not a runtime error.
    assert_eq!(0, result);
}

fn get_page_size() -> usize {
    let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };

    // Assert that the page size is a power of two, which is assumed when the page size is used.
    assert!(page_size != 0);
    assert_eq!(0, page_size & (page_size - 1));

    page_size
}

/// Rounds `size` up to the nearest multiple of `power_of_two`.
fn round_up_to(size: usize, power_of_two: usize) -> usize {
    (size + (power_of_two - 1)) & !(power_of_two - 1)
}

#[test]
fn verify_round_up_to() {
    assert_eq!(1024, round_up_to(23, 1024));
    assert_eq!(1024, round_up_to(1024, 1024));
    assert_eq!(2048, round_up_to(1025, 1024));
}

/// Rounds `size` down to the nearest multiple of `power_of_two`.
fn round_down_to(size: usize, power_of_two: usize) -> usize {
    size & !(power_of_two - 1)
}

#[test]
fn verify_round_down_to() {
    assert_eq!(0, round_down_to(23, 1024));
    assert_eq!(1024, round_down_to(1024, 1024));
    assert_eq!(1024, round_down_to(1025, 1024));
}

impl StreamBuffer {
    /// Maps the file at `path` into memory.
    ///
    /// TODO: Document what happens when the file is changed after opening.
    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<StreamBuffer> {
        // Open the `fs::File` so we get all of std's error handling for free, then use it to
        // extract the file descriptor. The file is closed again when it goes out of scope, but
        // `mmap` only requires the descriptor to be open for the `mmap` call, so this is fine.
        let file = try!(fs::File::open(path));
        let (buffer, length) = try!(map_file(&file));
        let fstream = StreamBuffer {
            page_size: get_page_size(),
            buffer: buffer,
            length: length,
        };
        Ok(fstream)
    }

    /// Returns the length of the mapped file in bytes.
    pub fn len(&self) -> usize {
        self.length
    }

    /// Returns the number of bytes resident in physical memory, starting from `offset`.
    ///
    /// The slice `[offset..offset + resident_len]` can be accessed without causing page faults or
    /// disk access. Note that this is only a snapshot, and the kernel might decide to evict pages
    /// or make them resident at any time.
    ///
    /// The returned resident length is at most `length`.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    pub fn resident_len(&self, offset: usize, length: usize) -> usize {
        // The specified offset and length must lie within the buffer.
        assert!(offset + length <= self.length);

        let aligned_offset = round_down_to(offset, self.page_size);
        let aligned_length = round_up_to(length + (offset - aligned_offset), self.page_size);
        let num_pages = aligned_length / self.page_size;

        // There is a tradeoff here: to store residency information, we need an array of booleans.
        // The requested range can potentially be very large and it is only known at runtime. We
        // could allocate a vector here, but that requires a heap allocation just to get residency
        // information (which might in turn cause a page fault). Instead, check at most 32 pages at
        // once. This means more syscalls for large ranges, but it saves us the heap allocation,
        // and for ranges up to 32 pages (128 KiB typically) there is only one syscall.
        let mut residency = [false; 32];
        let mut pages_checked = 0;
        let mut pages_resident = 0;

        while pages_checked < num_pages {
            let pages_to_check = cmp::min(32, num_pages - pages_checked);
            let check_offset = (aligned_offset + pages_checked * self.page_size) as isize;
            let check_buffer = unsafe { self.buffer.offset(check_offset) };
            let check_length = pages_to_check * self.page_size;
            get_resident(check_buffer, check_length, &mut residency);

            // Count the number of resident pages.
            match residency[..pages_to_check].iter().position(|resident| !resident) {
                Some(non_resident) => {
                    // The index of the non-resident page is the number of resident pages.
                    pages_resident += non_resident;
                    break;
                }
                None => {
                    pages_resident += pages_to_check;
                    pages_checked += pages_to_check;
                }
            }
        }

        let resident_length = pages_resident * self.page_size + aligned_offset - offset;

        // Never return more than the requested length. The resident length might be larger than
        // the length of the buffer, because it is rounded up to the page size.
        cmp::min(length, resident_length)
    }

    /// Returns a good chunk length if the file is to be processed in chunks.
    ///
    /// This just returns the page size. Multiples of this are fine too.
    pub fn chunk_len_hint(&self) -> usize {
        self.page_size
    }

    /// Returns the file contents as a slice.
    ///
    /// Accessing elements of the slice might cause a page fault, blocking until the data has been
    /// read from disk.
    ///
    /// To avoid blocking, call `prefetch()` and check whether the memory is resident with
    /// `resident_len()`. The `try_slice()` method is similar to `as_slice()`, but it returns
    /// `None` if accessing the slice would block.
    pub fn as_slice(&self) -> &[u8] {
        unsafe { slice::from_raw_parts(self.buffer, self.length) }
    }

    /// Returns a slice if the requested range is resident in physical memory.
    ///
    /// If the slice is not resident, `prefetch()` is called, so that if the same slice is
    /// requested after a while, it likely is resident.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    pub fn try_slice(&self, offset: usize, length: usize) -> Option<&[u8]> {
        // The bounds check assertion is done in `resident_len()`, no need to duplicate it here.
        if self.resident_len(offset, length) < length {
            self.prefetch(offset, length);
            None
        } else {
            Some(&self.as_slice()[offset..offset + length])
        }
    }

    /// Advises the kernel to make a slice of the file resident in physical memory.
    ///
    /// This method does not block, meaning that when the function returns, the slice is not
    /// necessarily resident. After this function returns, the kernel may read the requested slice
    /// from disk and make it resident. Note that this is only an advice, the kernel need not honor
    /// it.
    ///
    /// To check whether the slice is resident at a later time, use `resident_len()`.
    ///
    /// # Panics
    ///
    /// Panics if the specified range lies outside of the buffer.
    pub fn prefetch(&self, offset: usize, length: usize) {
        // The specified offset and length must lie within the buffer.
        assert!(offset + length <= self.length);

        let aligned_offset = round_down_to(offset, self.page_size);
        let aligned_length = round_up_to(length + (offset - aligned_offset), self.page_size);

        let buffer = unsafe { self.buffer.offset(aligned_offset as isize) };
        prefetch(buffer, aligned_length);
    }
}

impl Drop for StreamBuffer {
    fn drop(&mut self) {
        unmap_file(self.buffer, self.length);
    }
}

#[test]
fn open_file() {
    let fstream = StreamBuffer::open("src/lib.rs");
    assert!(fstream.is_ok());
}

#[test]
fn make_resident() {
    let fstream = StreamBuffer::open("src/lib.rs").unwrap();

    // Touch the first page to make it resident.
    assert_eq!(fstream.as_slice()[3..15], b"Streambuffer"[..]);

    // Now at least that part should be resident.
    assert_eq!(fstream.resident_len(3, 12), 12);

    // If it is resident, `try_slice` should return a slice.
    assert_eq!(fstream.try_slice(3, 12), Some(&b"Streambuffer"[..]));
}

#[test]
fn prefetch_is_not_harmful() {
    let fstream = StreamBuffer::open("src/lib.rs").unwrap();

    // It is impossible to test that this actually works without root access to instruct the kernel
    // to drop its caches, but at least we can verify that calling `prefetch` is not harmful.
    fstream.prefetch(0, fstream.len());

    // Reading from the file should still work as normal.
    assert_eq!(fstream.as_slice()[3..15], b"Streambuffer"[..]);
}
<|endoftext|>


<commit_before>#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

extern crate libc;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

#[cfg(target_os="windows")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

mod ffi {
    pub use libc::{c_char, c_int, uint64_t};

    extern "C" {
        pub fn nvim_main (argc: c_int, argv: *const *const c_char) -> c_int;
        pub fn channel_from_fds (read_fd: c_int, write_fd: c_int) -> uint64_t;
        pub fn channel_subscribe (id: uint64_t, event: *const c_char);
    }
}

pub fn nvim_main() -> i32 {
    unsafe {
        let args = ["nvim".to_c_str().as_ptr()];
        ffi::nvim_main(args.len() as i32, args.as_ptr())
    }
}

pub struct Channel {
    id: ffi::uint64_t
}

impl Channel {
    pub fn new(read_fd: i32, write_fd: i32) -> Channel {
        Channel {
            id: unsafe { ffi::channel_from_fds(read_fd, write_fd) }
        }
    }

    pub fn subscribe(&mut self, event: &str) {
        unsafe { ffi::channel_subscribe(self.id, event.to_c_str().as_ptr()) }
    }
}
<commit_msg>Allow passing args to nvim_main<commit_after>#![crate_name = "neovim"]
#![crate_type = "lib"]
#![crate_type = "rlib"]

extern crate libc;

use std::c_str::CString;
use std::vec::Vec;

#[cfg(target_os="macos")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    #[link(name = "iconv")]
    extern{}
}

#[cfg(target_os="linux")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

#[cfg(target_os="windows")]
mod platform {
    #[link(name = "nvim")]
    #[link(name = "uv")]
    #[link(name = "msgpack")]
    #[link(name = "curses")]
    extern{}
}

mod ffi {
    pub use libc::{c_char, c_int, uint64_t};

    extern "C" {
        pub fn nvim_main (argc: c_int, argv: *const *const c_char) -> c_int;
        pub fn channel_from_fds (read_fd: c_int, write_fd: c_int) -> uint64_t;
        pub fn channel_subscribe (id: uint64_t, event: *const c_char);
    }
}

pub fn nvim_main(args: &[&str]) -> i32 {
    let v: Vec<CString> = args.iter().map(|s| s.to_c_str()).collect();
    let vp: Vec<*const ffi::c_char> = v.iter().map(|s| s.as_ptr()).collect();
    let p_vp: *const *const ffi::c_char = vp.as_ptr();

    unsafe { ffi::nvim_main(vp.len() as i32, p_vp) }
}

pub struct Channel {
    id: ffi::uint64_t
}

impl Channel {
    pub fn new(read_fd: i32, write_fd: i32) -> Channel {
        Channel {
            id: unsafe { ffi::channel_from_fds(read_fd, write_fd) }
        }
    }

    pub fn subscribe(&mut self, event: &str) {
        unsafe { ffi::channel_subscribe(self.id, event.to_c_str().as_ptr()) }
    }
}
<|endoftext|>