<commit_before>//! Iron's HTTP Response representation and associated methods.

use std::io::{IoResult, File, MemReader};
use std::path::BytesContainer;

use typemap::TypeMap;
use plugin::Extensible;
use super::status;
use super::status::Status;
use http::headers::response::HeaderCollection;
use http::headers::content_type::MediaType;

pub use http::server::response::ResponseWriter as HttpResponse;

use contenttype::get_content_type;

/// The response representation given to `Middleware`
pub struct Response {
    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Option<Box<Reader>>,

    /// The headers of the response.
    pub headers: Box<HeaderCollection>,

    /// The response status-code.
    pub status: Option<Status>,

    /// A TypeMap to be used as an extensible storage for data
    /// associated with this Response.
    pub extensions: TypeMap
}

impl Response {
    /// Construct a blank Response
    pub fn new() -> Response {
        Response {
            headers: box HeaderCollection::new(),
            status: None, // Start with no response code.
            body: None, // Start with no body.
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the status.
    pub fn status(status: status::Status) -> Response {
        Response {
            body: None,
            headers: box HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the specified body and status.
    pub fn with<B: BytesContainer>(status: status::Status, body: B) -> Response {
        Response {
            body: Some(box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader>),
            headers: box HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Write the `Status` and data to the `Response`.
    pub fn serve<S: BytesContainer>(&mut self, status: Status, body: S) {
        self.status = Some(status);
        self.body = Some(box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader>);
    }

    /// Serve the file located at `path`.
    ///
    /// This usually means a request has been handled, and `Middleware`
    /// may want to `Unwind` after a file is served. If the status should be
    /// anything other than `200`, `Middleware` must set it, including in
    /// the case of an `Err`.
    ///
    /// `serve_file` will error if the file does not exist, the process
    /// does not have correct permissions, or it has other issues in reading
    /// from the file. `Middleware` should handle this gracefully.
    pub fn serve_file(&mut self, path: &Path) -> IoResult<()> {
        let file = try!(File::open(path));
        self.headers.content_type = path.extension_str().and_then(get_content_type);
        self.body = Some(box file as Box<Reader>);
        self.status = Some(status::Ok);
        Ok(())
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(self, http_res: &mut HttpResponse) {
        http_res.headers = self.headers.clone();

        // Default to a 404 if no response code was set
        http_res.status = self.status.clone().unwrap_or(status::NotFound);

        // Read the body into the http_res body
        let mut body = self.body.unwrap_or_else(|| box MemReader::new(vec![]) as Box<Reader>);
        let _ = match body.read_to_end() {
            Ok(body_content) => {
                let plain_txt = MediaType {
                    type_: "text".to_string(),
                    subtype: "plain".to_string(),
                    parameters: vec![]
                };

                // Set content length and type
                http_res.headers.content_length =
                    Some(body_content.len());
                http_res.headers.content_type =
                    Some(http_res.headers.content_type.clone().unwrap_or(plain_txt));

                // Write the body
                http_res.write(body_content.as_slice())
            },
            Err(e) => Err(e)
        // Catch errors from reading + writing
        }.map_err(|e| {
            error!("Error reading/writing body: {}", e);
            http_res.status = status::InternalServerError;
            let _ = http_res.write(b"Internal Server Error")
                // Something is really, really wrong.
                .map_err(|e| error!("Error writing error message: {}", e));
        });
    }
}

// Allow plugins to attach to responses.
impl Extensible for Response {
    fn extensions(&self) -> &TypeMap {
        &self.extensions
    }

    fn extensions_mut(&mut self) -> &mut TypeMap {
        &mut self.extensions
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_content_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<commit_msg>(feat) Added lifetime bounds for trait objects.<commit_after>//! Iron's HTTP Response representation and associated methods.

use std::io::{IoResult, File, MemReader};
use std::path::BytesContainer;

use typemap::TypeMap;
use plugin::Extensible;
use super::status;
use super::status::Status;
use http::headers::response::HeaderCollection;
use http::headers::content_type::MediaType;

pub use http::server::response::ResponseWriter as HttpResponse;

use contenttype::get_content_type;

/// The response representation given to `Middleware`
pub struct Response {
    /// The body of the response.
    ///
    /// This is a Reader for generality, most data should
    /// be sent using either `serve` or `serve_file`.
    ///
    /// Arbitrary Readers can be sent by assigning to body.
    pub body: Option<Box<Reader + Send>>,

    /// The headers of the response.
    pub headers: Box<HeaderCollection>,

    /// The response status-code.
    pub status: Option<Status>,

    /// A TypeMap to be used as an extensible storage for data
    /// associated with this Response.
    pub extensions: TypeMap
}

impl Response {
    /// Construct a blank Response
    pub fn new() -> Response {
        Response {
            headers: box HeaderCollection::new(),
            status: None, // Start with no response code.
            body: None, // Start with no body.
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the status.
    pub fn status(status: status::Status) -> Response {
        Response {
            body: None,
            headers: box HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Create a new response with the specified body and status.
    pub fn with<B: BytesContainer>(status: status::Status, body: B) -> Response {
        Response {
            body: Some(box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader + Send>),
            headers: box HeaderCollection::new(),
            status: Some(status),
            extensions: TypeMap::new()
        }
    }

    /// Write the `Status` and data to the `Response`.
    pub fn serve<S: BytesContainer>(&mut self, status: Status, body: S) {
        self.status = Some(status);
        self.body = Some(box MemReader::new(body.container_as_bytes().to_vec()) as Box<Reader + Send>);
    }

    /// Serve the file located at `path`.
    ///
    /// This usually means a request has been handled, and `Middleware`
    /// may want to `Unwind` after a file is served. If the status should be
    /// anything other than `200`, `Middleware` must set it, including in
    /// the case of an `Err`.
    ///
    /// `serve_file` will error if the file does not exist, the process
    /// does not have correct permissions, or it has other issues in reading
    /// from the file. `Middleware` should handle this gracefully.
    pub fn serve_file(&mut self, path: &Path) -> IoResult<()> {
        let file = try!(File::open(path));
        self.headers.content_type = path.extension_str().and_then(get_content_type);
        self.body = Some(box file as Box<Reader + Send>);
        self.status = Some(status::Ok);
        Ok(())
    }

    // `write_back` is used to put all the data added to `self`
    // back onto an `HttpResponse` so that it is sent back to the
    // client.
    //
    // `write_back` consumes the `Response`.
    #[doc(hidden)]
    pub fn write_back(self, http_res: &mut HttpResponse) {
        http_res.headers = self.headers.clone();

        // Default to a 404 if no response code was set
        http_res.status = self.status.clone().unwrap_or(status::NotFound);

        // Read the body into the http_res body
        let mut body = self.body.unwrap_or_else(|| box MemReader::new(vec![]) as Box<Reader + Send>);
        let _ = match body.read_to_end() {
            Ok(body_content) => {
                let plain_txt = MediaType {
                    type_: "text".to_string(),
                    subtype: "plain".to_string(),
                    parameters: vec![]
                };

                // Set content length and type
                http_res.headers.content_length =
                    Some(body_content.len());
                http_res.headers.content_type =
                    Some(http_res.headers.content_type.clone().unwrap_or(plain_txt));

                // Write the body
                http_res.write(body_content.as_slice())
            },
            Err(e) => Err(e)
        // Catch errors from reading + writing
        }.map_err(|e| {
            error!("Error reading/writing body: {}", e);
            http_res.status = status::InternalServerError;
            let _ = http_res.write(b"Internal Server Error")
                // Something is really, really wrong.
                .map_err(|e| error!("Error writing error message: {}", e));
        });
    }
}

// Allow plugins to attach to responses.
impl Extensible for Response {
    fn extensions(&self) -> &TypeMap {
        &self.extensions
    }

    fn extensions_mut(&mut self) -> &mut TypeMap {
        &mut self.extensions
    }
}

#[test]
fn matches_content_type () {
    let path = &Path::new("test.txt");
    let content_type = path.extension_str().and_then(get_content_type).unwrap();

    assert_eq!(content_type.type_.as_slice(), "text");
    assert_eq!(content_type.subtype.as_slice(), "plain");
}
<|endoftext|>


<commit_before>use rustc_serialize::json;

use common::{Nullable, Date};
use cookie;
use time;
use std::collections::BTreeMap;

#[derive(Debug)]
pub enum WebDriverResponse {
    NewSession(NewSessionResponse),
    DeleteSession,
    WindowSize(WindowSizeResponse),
    WindowPosition(WindowPositionResponse),
    ElementRect(ElementRectResponse),
    Cookie(CookieResponse),
    Generic(ValueResponse),
    Void
}

impl WebDriverResponse {
    pub fn to_json_string(self) -> String {
        match self {
            WebDriverResponse::NewSession(x) => json::encode(&x),
            WebDriverResponse::DeleteSession => Ok("{}".to_string()),
            WebDriverResponse::WindowSize(x) => json::encode(&x),
            WebDriverResponse::WindowPosition(x) => json::encode(&x),
            WebDriverResponse::ElementRect(x) => json::encode(&x),
            WebDriverResponse::Cookie(x) => json::encode(&x),
            WebDriverResponse::Generic(x) => json::encode(&x),
            WebDriverResponse::Void => Ok("{}".to_string())
        }.unwrap()
    }
}

#[derive(RustcEncodable, Debug)]
pub struct NewSessionResponse {
    pub sessionId: String,
    pub value: json::Json
}

impl NewSessionResponse {
    pub fn new(session_id: String, value: json::Json) -> NewSessionResponse {
        NewSessionResponse {
            value: value,
            sessionId: session_id
        }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct ValueResponse {
    pub value: json::Json
}

impl ValueResponse {
    pub fn new(value: json::Json) -> ValueResponse {
        ValueResponse {
            value: value
        }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct WindowSizeResponse {
    pub width: u64,
    pub height: u64
}

impl WindowSizeResponse {
    pub fn new(width: u64, height: u64) -> WindowSizeResponse {
        WindowSizeResponse {
            width: width,
            height: height
        }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct WindowPositionResponse {
    pub x: i64,
    pub y: i64,
}

impl WindowPositionResponse {
    pub fn new(x: i64, y: i64) -> WindowPositionResponse {
        WindowPositionResponse { x: x, y: y }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct ElementRectResponse {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64
}

impl ElementRectResponse {
    pub fn new(x: f64, y: f64, width: f64, height: f64) -> ElementRectResponse {
        ElementRectResponse {
            x: x,
            y: y,
            width: width,
            height: height
        }
    }
}

//TODO: some of these fields are probably supposed to be optional
#[derive(RustcEncodable, PartialEq, Debug, Clone)]
pub struct Cookie {
    pub name: String,
    pub value: String,
    pub path: Nullable<String>,
    pub domain: Nullable<String>,
    pub expiry: Nullable<Date>,
    pub secure: bool,
    pub httpOnly: bool
}

impl Cookie {
    pub fn new(name: String, value: String, path: Nullable<String>, domain: Nullable<String>,
               expiry: Nullable<Date>, secure: bool, http_only: bool) -> Cookie {
        Cookie {
            name: name,
            value: value,
            path: path,
            domain: domain,
            expiry: expiry,
            secure: secure,
            httpOnly: http_only
        }
    }
}

impl Into<cookie::Cookie> for Cookie {
    fn into(self) -> cookie::Cookie {
        cookie::Cookie {
            name: self.name,
            value: self.value,
            expires: match self.expiry {
                Nullable::Value(Date(expiry)) => {
                    Some(time::at(time::Timespec::new(expiry as i64, 0)))
                },
                Nullable::Null => None
            },
            max_age: None,
            domain: self.domain.into(),
            path: self.path.into(),
            secure: self.secure,
            httponly: self.httpOnly,
            custom: BTreeMap::new()
        }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct CookieResponse {
    pub value: Vec<Cookie>
}

impl CookieResponse {
    pub fn new(value: Vec<Cookie>) -> CookieResponse {
        CookieResponse {
            value: value
        }
    }
}
<commit_msg>response: add CloseWindow response type<commit_after>use rustc_serialize::json;

use common::{Nullable, Date};
use cookie;
use time;
use std::collections::BTreeMap;

#[derive(Debug)]
pub enum WebDriverResponse {
    CloseWindow(CloseWindowResponse),
    Cookie(CookieResponse),
    DeleteSession,
    ElementRect(ElementRectResponse),
    Generic(ValueResponse),
    NewSession(NewSessionResponse),
    Void,
    WindowPosition(WindowPositionResponse),
    WindowSize(WindowSizeResponse),
}

impl WebDriverResponse {
    pub fn to_json_string(self) -> String {
        match self {
            WebDriverResponse::CloseWindow(x) => json::encode(&x),
            WebDriverResponse::Cookie(x) => json::encode(&x),
            WebDriverResponse::DeleteSession => Ok("{}".to_string()),
            WebDriverResponse::ElementRect(x) => json::encode(&x),
            WebDriverResponse::Generic(x) => json::encode(&x),
            WebDriverResponse::NewSession(x) => json::encode(&x),
            WebDriverResponse::Void => Ok("{}".to_string()),
            WebDriverResponse::WindowPosition(x) => json::encode(&x),
            WebDriverResponse::WindowSize(x) => json::encode(&x),
        }.unwrap()
    }
}

#[derive(RustcEncodable, Debug)]
pub struct CloseWindowResponse {
    pub window_handles: Vec<String>,
}

impl CloseWindowResponse {
    pub fn new(handles: Vec<String>) -> CloseWindowResponse {
        CloseWindowResponse { window_handles: handles }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct NewSessionResponse {
    pub sessionId: String,
    pub value: json::Json
}

impl NewSessionResponse {
    pub fn new(session_id: String, value: json::Json) -> NewSessionResponse {
        NewSessionResponse {
            value: value,
            sessionId: session_id
        }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct ValueResponse {
    pub value: json::Json
}

impl ValueResponse {
    pub fn new(value: json::Json) -> ValueResponse {
        ValueResponse {
            value: value
        }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct WindowSizeResponse {
    pub width: u64,
    pub height: u64
}

impl WindowSizeResponse {
    pub fn new(width: u64, height: u64) -> WindowSizeResponse {
        WindowSizeResponse {
            width: width,
            height: height
        }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct WindowPositionResponse {
    pub x: i64,
    pub y: i64,
}

impl WindowPositionResponse {
    pub fn new(x: i64, y: i64) -> WindowPositionResponse {
        WindowPositionResponse { x: x, y: y }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct ElementRectResponse {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64
}

impl ElementRectResponse {
    pub fn new(x: f64, y: f64, width: f64, height: f64) -> ElementRectResponse {
        ElementRectResponse {
            x: x,
            y: y,
            width: width,
            height: height
        }
    }
}

//TODO: some of these fields are probably supposed to be optional
#[derive(RustcEncodable, PartialEq, Debug, Clone)]
pub struct Cookie {
    pub name: String,
    pub value: String,
    pub path: Nullable<String>,
    pub domain: Nullable<String>,
    pub expiry: Nullable<Date>,
    pub secure: bool,
    pub httpOnly: bool
}

impl Cookie {
    pub fn new(name: String, value: String, path: Nullable<String>, domain: Nullable<String>,
               expiry: Nullable<Date>, secure: bool, http_only: bool) -> Cookie {
        Cookie {
            name: name,
            value: value,
            path: path,
            domain: domain,
            expiry: expiry,
            secure: secure,
            httpOnly: http_only
        }
    }
}

impl Into<cookie::Cookie> for Cookie {
    fn into(self) -> cookie::Cookie {
        cookie::Cookie {
            name: self.name,
            value: self.value,
            expires: match self.expiry {
                Nullable::Value(Date(expiry)) => {
                    Some(time::at(time::Timespec::new(expiry as i64, 0)))
                },
                Nullable::Null => None
            },
            max_age: None,
            domain: self.domain.into(),
            path: self.path.into(),
            secure: self.secure,
            httponly: self.httpOnly,
            custom: BTreeMap::new()
        }
    }
}

#[derive(RustcEncodable, Debug)]
pub struct CookieResponse {
    pub value: Vec<Cookie>
}

impl CookieResponse {
    pub fn new(value: Vec<Cookie>) -> CookieResponse {
        CookieResponse {
            value: value
        }
    }
}
<|endoftext|>


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

use common::{Header, HTTPVersion, StatusCode};

use std::ascii::AsciiExt;
use std::cmp::Ordering;
use std::sync::mpsc::Receiver;

use std::io::{self, Read, Write, Cursor};
use std::io::Result as IoResult;

use std::fs::File;

use std::str::FromStr;

/// Object representing an HTTP response whose purpose is to be given to a `Request`.
///
/// Some headers cannot be changed. Trying to define the value
/// of one of these will have no effect:
///
///  - `Accept-Ranges`
///  - `Connection`
///  - `Content-Range`
///  - `Trailer`
///  - `Transfer-Encoding`
///  - `Upgrade`
///
/// Some headers have special behaviors:
///
///  - `Content-Encoding`: If you define this header, the library
///     will assume that the data from the `Read` object has the specified encoding
///     and will just pass-through.
///
///  - `Content-Length`: The length of the data should be set manually
///     using the `Reponse` object's API. Attempting to set the value of this
///     header will be equivalent to modifying the size of the data but the header
///     itself may not be present in the final result.
///
pub struct Response<R> where R: Read {
    reader: R,
    status_code: StatusCode,
    headers: Vec<Header>,
    data_length: Option<usize>,
}

/// A `Response` without a template parameter.
pub type ResponseBox = Response<Box<Read + Send>>;

/// Transfer encoding to use when sending the message.
/// Note that only *supported* encoding are listed here.
enum TransferEncoding {
    Identity,
    Chunked,
}

impl FromStr for TransferEncoding {
    type Err = ();

    fn from_str(input: &str) -> Result<TransferEncoding, ()> {
        if input.eq_ignore_ascii_case("identity") {
            Ok(TransferEncoding::Identity)
        } else if input.eq_ignore_ascii_case("chunked") {
            Ok(TransferEncoding::Chunked)
        } else {
            Err(())
        }
    }
}

/// Builds a Date: header with the current date.
fn build_date_header() -> Header {
    // FIXME: right date
    Header::from_bytes(&b"Date"[..], &b"Wed, 15 Nov 1995 06:25:24 GMT"[..]).unwrap()
}

fn write_message_header<W>(mut writer: W, http_version: &HTTPVersion,
                           status_code: &StatusCode, headers: &[Header])
                           -> IoResult<()> where W: Write
{
    // writing status line
    try!(write!(&mut writer, "HTTP/{}.{} {} {}\r\n",
        http_version.0,
        http_version.1,
        status_code.0,
        status_code.get_default_reason_phrase()
    ));

    // writing headers
    for header in headers.iter() {
        try!(writer.write_all(header.field.as_str().as_ref()));
        try!(write!(&mut writer, ": "));
        try!(writer.write_all(header.value.as_ref().as_ref()));
        try!(write!(&mut writer, "\r\n"));
    }

    // separator between header and data
    try!(write!(&mut writer, "\r\n"));

    Ok(())
}

fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
                            entity_length: &Option<usize>, has_additional_headers: bool)
    -> TransferEncoding
{
    use util;

    // HTTP 1.0 doesn't support other encoding
    if *http_version <= (1, 0) {
        return TransferEncoding::Identity;
    }

    // parsing the request's TE header
    let user_request = request_headers.iter()
        // finding TE
        .find(|h| h.field.equiv(&"TE"))

        // getting its value
        .map(|h| h.value.clone())

        // getting the corresponding TransferEncoding
        .and_then(|value| {
            // getting list of requested elements
            let mut parse = util::parse_header_value(value.as_str());     // TODO: remove conversion

            // sorting elements by most priority
            parse.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));

            // trying to parse each requested encoding
            for value in parse.iter() {
                // q=0 are ignored
                if value.1 <= 0.0 { continue }

                match <TransferEncoding as FromStr>::from_str(value.0) {
                    Ok(te) => return Some(te),
                    _ => ()     // unrecognized/unsupported encoding
                };
            }

            // encoding not found
            None
        });

    //
    if user_request.is_some() {
        return user_request.unwrap();
    }

    // if we have additional headers, using chunked
    if has_additional_headers {
        return TransferEncoding::Chunked;
    }

    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
    let chunks_threshold = 32768;
    if entity_length.as_ref().map_or(true, |val| *val >= chunks_threshold) {
        return TransferEncoding::Chunked;
    }

    // Identity by default
    TransferEncoding::Identity
}

impl<R> Response<R> where R: Read {
    /// Creates a new Response object.
    ///
    /// The `additional_headers` argument is a receiver that
    ///  may provide headers even after the response has been sent.
    ///
    /// All the other arguments are straight-forward.
    pub fn new(status_code: StatusCode, headers: Vec<Header>,
               data: R, data_length: Option<usize>,
               additional_headers: Option<Receiver<Header>>)
                -> Response<R>
    {
        let mut response = Response {
            reader: data,
            status_code: status_code,
            headers: Vec::with_capacity(16),
            data_length: data_length,
        };

        for h in headers.into_iter() {
            response.add_header(h)
        }

        // dummy implementation
        if let Some(additional_headers) = additional_headers {
            for h in additional_headers.iter() {
                response.add_header(h)
            }
        }

        response
    }

    /// Adds a header to the list.
    /// Does all the checks.
    pub fn add_header<H>(&mut self, header: H) where H: Into<Header> {
        let header = header.into();

        // ignoring forbidden headers
        if header.field.equiv(&"Accept-Ranges") ||
           header.field.equiv(&"Connection") ||
           header.field.equiv(&"Content-Range") ||
           header.field.equiv(&"Trailer") ||
           header.field.equiv(&"Transfer-Encoding") ||
           header.field.equiv(&"Upgrade")
        {
            return;
        }

        // if the header is Content-Length, setting the data length
        if header.field.equiv(&"Content-Length") {
            match <usize as FromStr>::from_str(header.value.as_str()) {
                Ok(val) => self.data_length = Some(val),
                Err(_) => ()      // wrong value for content-length
            };

            return;
        }

        self.headers.push(header);
    }

    /// Returns the same request, but with an additional header.
    ///
    /// Some headers cannot be modified and some other have a
    ///  special behavior. See the documentation above.
    #[inline]
    pub fn with_header<H>(mut self, header: H) -> Response<R> where H: Into<Header> {
        self.add_header(header.into());
        self
    }

    /// Returns the same request, but with a different status code.
    #[inline]
    pub fn with_status_code<S>(mut self, code: S) -> Response<R> where S: Into<StatusCode> {
        self.status_code = code.into();
        self
    }

    /// Returns the same request, but with different data.
    pub fn with_data<S>(self, reader: S, data_length: Option<usize>) -> Response<S> where S: Read {
        Response {
            reader: reader,
            headers: self.headers,
            status_code: self.status_code,
            data_length: data_length,
        }
    }

    /// Prints the HTTP response to a writer.
    ///
    /// This function is the one used to send the response to the client's socket.
    /// Therefore you shouldn't expect anything pretty-printed or even readable.
    ///
    /// The HTTP version and headers passed as arguments are used to
    ///  decide which features (most notably, encoding) to use.
    ///
    /// Note: does not flush the writer.
    pub fn raw_print<W: Write>(mut self, mut writer: W, http_version: HTTPVersion,
                               request_headers: &[Header], do_not_send_body: bool,
                               upgrade: Option<&str>)
                               -> IoResult<()>
    {
        let mut transfer_encoding = Some(choose_transfer_encoding(request_headers,
                                    &http_version, &self.data_length, false /* TODO */));

        // add `Date` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
            self.headers.insert(0, build_date_header());
        }

        // add `Server` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
            self.headers.insert(0,
                Header::from_bytes(&b"Server"[..], &b"tiny-http (Rust)"[..]).unwrap()
            );
        }

        // handling upgrade
        if upgrade.is_some() {
            let upgrade = upgrade.unwrap();
            self.headers.insert(0, Header::from_bytes(&b"Upgrade"[..], upgrade.as_bytes()).unwrap());
            self.headers.insert(0, Header::from_bytes(&b"Connection"[..], &b"upgrade"[..]).unwrap());
            transfer_encoding = None;
        }

        // checking whether to ignore the body of the response
        let do_not_send_body = do_not_send_body ||
            match self.status_code.0 {
                // sattus code 1xx, 204 and 304 MUST not include a body
                100...199 | 204 | 304 => true,
                _ => false
            };

        // preparing headers for transfer
        match transfer_encoding {
            Some(TransferEncoding::Chunked) => {
                self.headers.push(
                    Header::from_bytes(&b"Transfer-Encoding"[..], &b"chunked"[..]).unwrap()
                )
            },

            Some(TransferEncoding::Identity) => {
                assert!(self.data_length.is_some());
                let data_length = self.data_length.unwrap();

                self.headers.push(
                    Header::from_bytes(&b"Content-Length"[..], format!("{}", data_length).as_bytes()).unwrap()
                )
            },

            _ => ()
        };

        // sending headers
        try!(write_message_header(writer.by_ref(), &http_version,
            &self.status_code, &self.headers));

        // sending the body
        if !do_not_send_body {
            match transfer_encoding {

                Some(TransferEncoding::Chunked) => {
                    use chunked_transfer::Encoder;

                    let mut writer = Encoder::new(writer);
                    try!(io::copy(&mut self.reader, &mut writer));
                },

                Some(TransferEncoding::Identity) => {
                    use util::EqualReader;

                    assert!(self.data_length.is_some());
                    let data_length = self.data_length.unwrap();

                    if data_length >= 1 {
                        let (mut equ_reader, _) =
                            EqualReader::new(self.reader.by_ref(), data_length);
                        try!(io::copy(&mut equ_reader, &mut writer));
                    }
                },

                _ => ()

            }
        }

        Ok(())
    }
}

impl<R> Response<R> where R: Read + Send + 'static {
    /// Turns this response into a `Response<Box<Read + Send>>`.
    pub fn boxed(self) -> ResponseBox {
        Response {
            reader: Box::new(self.reader) as Box<Read + Send>,
            status_code: self.status_code,
            headers: self.headers,
            data_length: self.data_length,
        }
    }
}

impl Response<File> {
    /// Builds a new `Response` from a `File`.
    ///
    /// The `Content-Type` will **not** be automatically detected,
    ///  you must set it yourself.
    pub fn from_file(file: File) -> Response<File> {
        let file_size = file.metadata().ok().map(|v| v.len() as usize);

        Response::new(
            StatusCode(200),
            Vec::with_capacity(0),
            file,
            file_size,
            None,
        )
    }
}

impl Response<Cursor<Vec<u8>>> {
    pub fn from_data<D>(data: D) -> Response<Cursor<Vec<u8>>> where D: Into<Vec<u8>> {
        let data = data.into();
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            Vec::with_capacity(0),
            Cursor::new(data),
            Some(data_len),
            None,
        )
    }

    pub fn from_string<S>(data: S) -> Response<Cursor<Vec<u8>>> where S: Into<String> {
        let data = data.into();
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            vec![
                Header::from_bytes(&b"Content-Type"[..], &b"text/plain; charset=UTF-8"[..]).unwrap()
            ],
            Cursor::new(data.into_bytes()),
            Some(data_len),
            None,
        )
    }
}

impl Response<io::Empty> {
    /// Builds an empty `Response` with the given status code.
    pub fn empty<S>(status_code: S) -> Response<io::Empty> where S: Into<StatusCode> {
        Response::new(
            status_code.into(),
            Vec::with_capacity(0),
            io::empty(),
            Some(0),
            None,
        )
    }

    /// DEPRECATED. Use `empty` instead.
    pub fn new_empty(status_code: StatusCode) -> Response<io::Empty> {
        Response::empty(status_code)
    }
}

impl Clone for Response<io::Empty> {
    fn clone(&self) -> Response<io::Empty> {
        Response {
            reader: io::empty(),
            status_code: self.status_code.clone(),
            headers: self.headers.clone(),
            data_length: self.data_length.clone(),
        }
    }
}
<commit_msg>Remove unnecessary into_iter<commit_after>// Copyright 2015 The tiny-http Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use common::{Header, HTTPVersion, StatusCode};

use std::ascii::AsciiExt;
use std::cmp::Ordering;
use std::sync::mpsc::Receiver;

use std::io::{self, Read, Write, Cursor};
use std::io::Result as IoResult;

use std::fs::File;

use std::str::FromStr;

/// Object representing an HTTP response whose purpose is to be given to a `Request`.
///
/// Some headers cannot be changed. Trying to define the value
/// of one of these will have no effect:
///
///  - `Accept-Ranges`
///  - `Connection`
///  - `Content-Range`
///  - `Trailer`
///  - `Transfer-Encoding`
///  - `Upgrade`
///
/// Some headers have special behaviors:
///
///  - `Content-Encoding`: If you define this header, the library
///     will assume that the data from the `Read` object has the specified encoding
///     and will just pass-through.
///
///  - `Content-Length`: The length of the data should be set manually
///     using the `Reponse` object's API. Attempting to set the value of this
///     header will be equivalent to modifying the size of the data but the header
///     itself may not be present in the final result.
///
pub struct Response<R> where R: Read {
    reader: R,
    status_code: StatusCode,
    headers: Vec<Header>,
    data_length: Option<usize>,
}

/// A `Response` without a template parameter.
pub type ResponseBox = Response<Box<Read + Send>>;

/// Transfer encoding to use when sending the message.
/// Note that only *supported* encoding are listed here.
enum TransferEncoding {
    Identity,
    Chunked,
}

impl FromStr for TransferEncoding {
    type Err = ();

    fn from_str(input: &str) -> Result<TransferEncoding, ()> {
        if input.eq_ignore_ascii_case("identity") {
            Ok(TransferEncoding::Identity)
        } else if input.eq_ignore_ascii_case("chunked") {
            Ok(TransferEncoding::Chunked)
        } else {
            Err(())
        }
    }
}

/// Builds a Date: header with the current date.
fn build_date_header() -> Header {
    // FIXME: right date
    Header::from_bytes(&b"Date"[..], &b"Wed, 15 Nov 1995 06:25:24 GMT"[..]).unwrap()
}

fn write_message_header<W>(mut writer: W, http_version: &HTTPVersion,
                           status_code: &StatusCode, headers: &[Header])
                           -> IoResult<()> where W: Write
{
    // writing status line
    try!(write!(&mut writer, "HTTP/{}.{} {} {}\r\n",
        http_version.0,
        http_version.1,
        status_code.0,
        status_code.get_default_reason_phrase()
    ));

    // writing headers
    for header in headers.iter() {
        try!(writer.write_all(header.field.as_str().as_ref()));
        try!(write!(&mut writer, ": "));
        try!(writer.write_all(header.value.as_ref().as_ref()));
        try!(write!(&mut writer, "\r\n"));
    }

    // separator between header and data
    try!(write!(&mut writer, "\r\n"));

    Ok(())
}

fn choose_transfer_encoding(request_headers: &[Header], http_version: &HTTPVersion,
                            entity_length: &Option<usize>, has_additional_headers: bool)
    -> TransferEncoding
{
    use util;

    // HTTP 1.0 doesn't support other encoding
    if *http_version <= (1, 0) {
        return TransferEncoding::Identity;
    }

    // parsing the request's TE header
    let user_request = request_headers.iter()
        // finding TE
        .find(|h| h.field.equiv(&"TE"))

        // getting its value
        .map(|h| h.value.clone())

        // getting the corresponding TransferEncoding
        .and_then(|value| {
            // getting list of requested elements
            let mut parse = util::parse_header_value(value.as_str());     // TODO: remove conversion

            // sorting elements by most priority
            parse.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(Ordering::Equal));

            // trying to parse each requested encoding
            for value in parse.iter() {
                // q=0 are ignored
                if value.1 <= 0.0 { continue }

                match <TransferEncoding as FromStr>::from_str(value.0) {
                    Ok(te) => return Some(te),
                    _ => ()     // unrecognized/unsupported encoding
                };
            }

            // encoding not found
            None
        });

    //
    if user_request.is_some() {
        return user_request.unwrap();
    }

    // if we have additional headers, using chunked
    if has_additional_headers {
        return TransferEncoding::Chunked;
    }

    // if we don't have a Content-Length, or if the Content-Length is too big, using chunks writer
    let chunks_threshold = 32768;
    if entity_length.as_ref().map_or(true, |val| *val >= chunks_threshold) {
        return TransferEncoding::Chunked;
    }

    // Identity by default
    TransferEncoding::Identity
}

impl<R> Response<R> where R: Read {
    /// Creates a new Response object.
    ///
    /// The `additional_headers` argument is a receiver that
    ///  may provide headers even after the response has been sent.
    ///
    /// All the other arguments are straight-forward.
    pub fn new(status_code: StatusCode, headers: Vec<Header>,
               data: R, data_length: Option<usize>,
               additional_headers: Option<Receiver<Header>>)
                -> Response<R>
    {
        let mut response = Response {
            reader: data,
            status_code: status_code,
            headers: Vec::with_capacity(16),
            data_length: data_length,
        };

        for h in headers {
            response.add_header(h)
        }

        // dummy implementation
        if let Some(additional_headers) = additional_headers {
            for h in additional_headers.iter() {
                response.add_header(h)
            }
        }

        response
    }

    /// Adds a header to the list.
    /// Does all the checks.
    pub fn add_header<H>(&mut self, header: H) where H: Into<Header> {
        let header = header.into();

        // ignoring forbidden headers
        if header.field.equiv(&"Accept-Ranges") ||
           header.field.equiv(&"Connection") ||
           header.field.equiv(&"Content-Range") ||
           header.field.equiv(&"Trailer") ||
           header.field.equiv(&"Transfer-Encoding") ||
           header.field.equiv(&"Upgrade")
        {
            return;
        }

        // if the header is Content-Length, setting the data length
        if header.field.equiv(&"Content-Length") {
            match <usize as FromStr>::from_str(header.value.as_str()) {
                Ok(val) => self.data_length = Some(val),
                Err(_) => ()      // wrong value for content-length
            };

            return;
        }

        self.headers.push(header);
    }

    /// Returns the same request, but with an additional header.
    ///
    /// Some headers cannot be modified and some other have a
    ///  special behavior. See the documentation above.
    #[inline]
    pub fn with_header<H>(mut self, header: H) -> Response<R> where H: Into<Header> {
        self.add_header(header.into());
        self
    }

    /// Returns the same request, but with a different status code.
    #[inline]
    pub fn with_status_code<S>(mut self, code: S) -> Response<R> where S: Into<StatusCode> {
        self.status_code = code.into();
        self
    }

    /// Returns the same request, but with different data.
    pub fn with_data<S>(self, reader: S, data_length: Option<usize>) -> Response<S> where S: Read {
        Response {
            reader: reader,
            headers: self.headers,
            status_code: self.status_code,
            data_length: data_length,
        }
    }

    /// Prints the HTTP response to a writer.
    ///
    /// This function is the one used to send the response to the client's socket.
    /// Therefore you shouldn't expect anything pretty-printed or even readable.
    ///
    /// The HTTP version and headers passed as arguments are used to
    ///  decide which features (most notably, encoding) to use.
    ///
    /// Note: does not flush the writer.
    pub fn raw_print<W: Write>(mut self, mut writer: W, http_version: HTTPVersion,
                               request_headers: &[Header], do_not_send_body: bool,
                               upgrade: Option<&str>)
                               -> IoResult<()>
    {
        let mut transfer_encoding = Some(choose_transfer_encoding(request_headers,
                                    &http_version, &self.data_length, false /* TODO */));

        // add `Date` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Date")).is_none() {
            self.headers.insert(0, build_date_header());
        }

        // add `Server` if not in the headers
        if self.headers.iter().find(|h| h.field.equiv(&"Server")).is_none() {
            self.headers.insert(0,
                Header::from_bytes(&b"Server"[..], &b"tiny-http (Rust)"[..]).unwrap()
            );
        }

        // handling upgrade
        if upgrade.is_some() {
            let upgrade = upgrade.unwrap();
            self.headers.insert(0, Header::from_bytes(&b"Upgrade"[..], upgrade.as_bytes()).unwrap());
            self.headers.insert(0, Header::from_bytes(&b"Connection"[..], &b"upgrade"[..]).unwrap());
            transfer_encoding = None;
        }

        // checking whether to ignore the body of the response
        let do_not_send_body = do_not_send_body ||
            match self.status_code.0 {
                // sattus code 1xx, 204 and 304 MUST not include a body
                100...199 | 204 | 304 => true,
                _ => false
            };

        // preparing headers for transfer
        match transfer_encoding {
            Some(TransferEncoding::Chunked) => {
                self.headers.push(
                    Header::from_bytes(&b"Transfer-Encoding"[..], &b"chunked"[..]).unwrap()
                )
            },

            Some(TransferEncoding::Identity) => {
                assert!(self.data_length.is_some());
                let data_length = self.data_length.unwrap();

                self.headers.push(
                    Header::from_bytes(&b"Content-Length"[..], format!("{}", data_length).as_bytes()).unwrap()
                )
            },

            _ => ()
        };

        // sending headers
        try!(write_message_header(writer.by_ref(), &http_version,
            &self.status_code, &self.headers));

        // sending the body
        if !do_not_send_body {
            match transfer_encoding {

                Some(TransferEncoding::Chunked) => {
                    use chunked_transfer::Encoder;

                    let mut writer = Encoder::new(writer);
                    try!(io::copy(&mut self.reader, &mut writer));
                },

                Some(TransferEncoding::Identity) => {
                    use util::EqualReader;

                    assert!(self.data_length.is_some());
                    let data_length = self.data_length.unwrap();

                    if data_length >= 1 {
                        let (mut equ_reader, _) =
                            EqualReader::new(self.reader.by_ref(), data_length);
                        try!(io::copy(&mut equ_reader, &mut writer));
                    }
                },

                _ => ()

            }
        }

        Ok(())
    }
}

impl<R> Response<R> where R: Read + Send + 'static {
    /// Turns this response into a `Response<Box<Read + Send>>`.
    pub fn boxed(self) -> ResponseBox {
        Response {
            reader: Box::new(self.reader) as Box<Read + Send>,
            status_code: self.status_code,
            headers: self.headers,
            data_length: self.data_length,
        }
    }
}

impl Response<File> {
    /// Builds a new `Response` from a `File`.
    ///
    /// The `Content-Type` will **not** be automatically detected,
    ///  you must set it yourself.
    pub fn from_file(file: File) -> Response<File> {
        let file_size = file.metadata().ok().map(|v| v.len() as usize);

        Response::new(
            StatusCode(200),
            Vec::with_capacity(0),
            file,
            file_size,
            None,
        )
    }
}

impl Response<Cursor<Vec<u8>>> {
    pub fn from_data<D>(data: D) -> Response<Cursor<Vec<u8>>> where D: Into<Vec<u8>> {
        let data = data.into();
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            Vec::with_capacity(0),
            Cursor::new(data),
            Some(data_len),
            None,
        )
    }

    pub fn from_string<S>(data: S) -> Response<Cursor<Vec<u8>>> where S: Into<String> {
        let data = data.into();
        let data_len = data.len();

        Response::new(
            StatusCode(200),
            vec![
                Header::from_bytes(&b"Content-Type"[..], &b"text/plain; charset=UTF-8"[..]).unwrap()
            ],
            Cursor::new(data.into_bytes()),
            Some(data_len),
            None,
        )
    }
}

impl Response<io::Empty> {
    /// Builds an empty `Response` with the given status code.
    pub fn empty<S>(status_code: S) -> Response<io::Empty> where S: Into<StatusCode> {
        Response::new(
            status_code.into(),
            Vec::with_capacity(0),
            io::empty(),
            Some(0),
            None,
        )
    }

    /// DEPRECATED. Use `empty` instead.
    pub fn new_empty(status_code: StatusCode) -> Response<io::Empty> {
        Response::empty(status_code)
    }
}

impl Clone for Response<io::Empty> {
    fn clone(&self) -> Response<io::Empty> {
        Response {
            reader: io::empty(),
            status_code: self.status_code.clone(),
            headers: self.headers.clone(),
            data_length: self.data_length.clone(),
        }
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![macro_escape]

use linalg::{ColVec, Mat, RowVec};

pub fn col(length: uint) -> ColVec<uint> {
    ColVec::from_fn(length, |i| i)
}

pub fn mat((nrows, ncols): (uint, uint)) -> Mat<(uint, uint)> {
    Mat::from_fn((nrows, ncols), |i| i).unwrap()
}

pub fn row(length: uint) -> RowVec<uint> {
    RowVec::from_fn(length, |i| i)
}

pub mod rand {
    use std::rand::{Rand, Rng, XorShiftRng, mod};

    use linalg::{ColVec, Mat, RowVec};

    pub fn col<T>(length: uint) -> ColVec<T> where T: Rand {
        let ref mut rng: XorShiftRng = rand::task_rng().gen();

        ColVec::rand(length, rng)
    }

    pub fn mat<T>((nrows, ncols): (uint, uint)) -> Mat<T> where T: Rand {
        let ref mut rng: XorShiftRng = rand::task_rng().gen();

        Mat::rand((nrows, ncols), rng).unwrap()
    }

    pub fn row<T>(length: uint) -> RowVec<T> where T: Rand {
        let ref mut rng: XorShiftRng = rand::task_rng().gen();

        RowVec::rand(length, rng)
    }
}

macro_rules! enforce {
    ($($e:expr),+,) => {
        if $(!$e)||+ { return TestResult::discard() }
    }
}

macro_rules! test {
    ($e:expr) => {
        (|| Ok::<_, ::linalg::Error>(TestResult::from_bool($e)))().unwrap()
    }
}

macro_rules! validate_diag {
    ($diag:expr, $size:expr) => {{
        let (nrows, ncols) = $size;
        let diag = $diag;

        if diag > 0 {
            let diag = diag as uint;

            enforce! {
                diag < ncols,
            }
        } else {
            let diag = -diag as uint;

            enforce! {
                diag < nrows,
            }
        }
    }}
}

macro_rules! validate_diag_index {
    ($diag:expr, $size:expr, $idx:expr) => {{
        let diag = $diag;
        let (nrows, ncols) = $size;
        let idx = $idx;

        if diag > 0 {
            let diag = diag as uint;

            enforce! {
                diag < ncols,
                idx < ::std::cmp::min(ncols - diag, nrows),
            }
        } else {
            let diag = -diag as uint;

            enforce! {
                diag < nrows,
                idx < ::std::cmp::min(nrows - diag, ncols),
            }
        }
    }}
}
<commit_msg>fixed tests<commit_after>#![allow(dead_code)]
#![macro_escape]

use linalg::{ColVec, Mat, RowVec};

pub fn col(length: uint) -> ColVec<uint> {
    ColVec::from_fn(length, |i| i)
}

pub fn mat((nrows, ncols): (uint, uint)) -> Mat<(uint, uint)> {
    Mat::from_fn((nrows, ncols), |i| i).unwrap()
}

pub fn row(length: uint) -> RowVec<uint> {
    RowVec::from_fn(length, |i| i)
}

pub mod rand {
    use std::rand::{Rand, Rng, XorShiftRng, self};

    use linalg::{ColVec, Mat, RowVec};

    pub fn col<T>(length: uint) -> ColVec<T> where T: Rand {
        let ref mut rng: XorShiftRng = rand::thread_rng().gen();

        ColVec::rand(length, rng)
    }

    pub fn mat<T>((nrows, ncols): (uint, uint)) -> Mat<T> where T: Rand {
        let ref mut rng: XorShiftRng = rand::thread_rng().gen();

        Mat::rand((nrows, ncols), rng).unwrap()
    }

    pub fn row<T>(length: uint) -> RowVec<T> where T: Rand {
        let ref mut rng: XorShiftRng = rand::thread_rng().gen();

        RowVec::rand(length, rng)
    }
}

macro_rules! enforce {
    ($($e:expr),+,) => {
        if $(!$e)||+ { return TestResult::discard() }
    }
}

macro_rules! test {
    ($e:expr) => {
        (|| Ok::<_, ::linalg::Error>(TestResult::from_bool($e)))().unwrap()
    }
}

macro_rules! validate_diag {
    ($diag:expr, $size:expr) => {{
        let (nrows, ncols) = $size;
        let diag = $diag;

        if diag > 0 {
            let diag = diag as uint;

            enforce! {
                diag < ncols,
            }
        } else {
            let diag = -diag as uint;

            enforce! {
                diag < nrows,
            }
        }
    }}
}

macro_rules! validate_diag_index {
    ($diag:expr, $size:expr, $idx:expr) => {{
        let diag = $diag;
        let (nrows, ncols) = $size;
        let idx = $idx;

        if diag > 0 {
            let diag = diag as uint;

            enforce! {
                diag < ncols,
                idx < ::std::cmp::min(ncols - diag, nrows),
            }
        } else {
            let diag = -diag as uint;

            enforce! {
                diag < nrows,
                idx < ::std::cmp::min(nrows - diag, ncols),
            }
        }
    }}
}
<|endoftext|>


<commit_before>#![feature(slice_patterns)]
use std::env;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;

#[derive(Clone)]
enum Expr {
  Int(i64),
  Str(String)
}

// XXX this should probably be split up. The VM has the instructions, a "Thread" has locals, stack, and arguments.
struct VM<'a> {
  instructions: Vec<&'a str>,
  locals: HashMap<&'a str, Expr>,
  labels: HashMap<&'a str, usize>,
}

fn parse_labels<'a>(instructions: &Vec<&'a str>) -> HashMap<&'a str, usize> {
  let mut map = HashMap::new();
  for (i, instr) in instructions.iter().enumerate() {
    if instr.starts_with("$label") {
      match instr.split(' ').nth(1) {
        Some(label) => map.insert(label, i),
        None => panic!("VM error: missing label"),
      };
    }
  };
  map
}

impl<'a> VM<'a> {
  pub fn new(instructions: Vec<&'a str>) -> VM {
    VM {
      labels: parse_labels(&instructions),
      instructions: instructions,
      locals: HashMap::new(),
    }
  }

  pub fn run(&mut self) {
    let ip = self.labels.get("main").unwrap_or(&0).clone();
    self.run_call(ip, &mut Vec::new());
  }

  // TODO locals
  pub fn run_call(&mut self, start_ip: usize, stack: &mut Vec<Expr>) -> Option<Expr> {
    let mut ip = start_ip;
    let len = self.instructions.len();

    let mut carry = false; // comparison carry

    let mut arguments = Vec::new();

    while ip < len {
      let instr = self.instructions[ip];
      ip += 1;
      if instr.starts_with("#") || instr.trim() == "" {
        continue;
      }
      match instr.split(" ").collect::<Vec<_>>().as_slice() {
        ["$label", _] => (),
        ["push", "int", n] => stack.push(Expr::Int(n.parse::<i64>().unwrap())),
        ["push", "str", n] => stack.push(Expr::Str(String::from(n))),
        ["push", "arg"] => arguments.push(stack.pop().expect("Nothing on the stack to push to the arguments")),

        ["dup"] => {
          let expr = stack.pop().expect("Nothing on the stack to pop");
          stack.push(expr.clone());
          stack.push(expr);
        },

        // convert to string
        ["convert", "int", "str"] => if let Some(&Expr::Int(arg)) = stack.last() {
          stack.push(Expr::Str(arg.to_string()));
        } else {
          panic!("VM error: cannot convert int to string");
        },

        ["convert", from, to] => panic!("VM error: cannot convert from {} to {}", from, to),

        // how = "always" | "carry"
        ["jump", how, n] => {
          let new_ip = if n.starts_with("$") {
            self.labels.get(n.trim_matches('$')).expect("VM error: no such label").clone()
          } else {
            n.parse::<usize>().unwrap()
          };

          if new_ip < len {
            // only check carry if we are conditionally jumping
            if how == "always" || (how == "carry" && carry) {
              ip = new_ip;
            }
          } else {
            panic!("VM error: trying to jump past end of bytecode ({} > {})", new_ip, len);
          }
        },

        ["carry", "set", val] => carry = val == "true",
        ["carry", "invert"] => carry = !carry,

        ["call", "primitive", "say"] => if let Some(Expr::Str(arg)) = stack.pop() {
          println!("hey {}", arg);
        } else {
          panic!("VM error: incorrect arguments to `say`");
        },

        // TODO resolve name in scope, check type, apply
        ["call", name] => {
          let new_ip = self.labels.get(name).expect("VM error: no such label").clone();
          let ret = self.run_call(new_ip, &mut arguments);
          arguments = Vec::new();
          match ret {
            Some(val) => stack.push(val),
            None => (),
          };
        },

        ["ret"] => {
          return None;
        },

        ["ret", "val"] => {
          if stack.len() != 1 {
            panic!("VM error: trying to return a value but the stack size is not 1 ({})", stack.len());
          }
          return stack.pop();
        },

        // NOTE: it's stack[*-1] OP stack[*-2]
        // which means if have stack=[1, 2]
        // you'll have 2 OP 1
        /// XXX this should JUST be a call!
        ["math", op] => if let
            (Some(Expr::Int(arg1)), Some(Expr::Int(arg2)))
            =
            (stack.pop(), stack.pop()) {
          let value = match op {
            "+" => arg1 + arg2,
            "-" => arg1 + arg2,
            //"/" => TODO. this should have very specific rules...
            "*" => arg1 * arg2,
            _ => panic!("VM error: unrecognized `op` operator: {}", op),
          };
          stack.push(Expr::Int(value));
        } else {
          panic!("VM error: not enough arguments to `add`");
        },

        // same note about "2 OP 1"
        // XXX same note about how this should be a `call`
        ["cmp", op] => if let
            (Some(Expr::Int(arg1)), Some(Expr::Int(arg2)))
            =
            (stack.pop(), stack.pop()) {
          carry = match op {
            "<" => arg1 < arg2,
            ">" => arg1 > arg2,
            "<=" => arg1 <= arg2,
            ">=" => arg1 >= arg2,
            "=" => arg1 == arg2,
            _ => panic!("VM error: unrecognized `cmp` operator: {}", op),
          };
        } else {
          panic!("VM error: not enough arguments to `cmp`");
        },

        // TODO this *shouldn't* be an identifier
        //      the backend should transform SSAF to tons of load/store
        //      (probably)
        //      (see backend#1)
        ["local", "load", name] => match self.locals.get(name) {
          Some(expr) => stack.push(expr.clone()),
          None       => panic!("VM error: unknown local variable {}", name),
        },

        ["local", "store", name] => if let Some(arg) = stack.pop() {
          // XXX check
          let _ = self.locals.insert(name, arg);
        } else {
          panic!("VM error: not enough arguments for `local store`");
        },

        [instr, ..] => panic!("VM error: no such instruction {}", instr),
        [..] => panic!("VM error: Unrecognized instruction!"),
      }
    }

    None
  }
}

fn main() {
	if let Some(path) = env::args().nth(1) {
		println!("Loading file {}", path);

    match File::open(&path) {
      Ok(mut file) => {
        let mut content = String::new();
        if let Ok(_) = file.read_to_string(&mut content) {
          // remove trailing newlines, split by line
          let instructions = content.trim_matches('\n').split('\n').collect();
          let mut vm = VM::new(instructions);
          vm.run();
        }
      },
      Err(err) => {
        panic!("Can't read file {} (error: {})", path, err);
      }
    }
	} else {
    panic!("Please give a file argument");
  }

  //     let path = Path::new("chry.fa");
  // let mut file = BufferedReader::new(File::open(&path));
  // for line in file.lines() {
  //  print!("{}", line.unwrap());
  //      }
}
<commit_msg>Fix stalled error message<commit_after>#![feature(slice_patterns)]
use std::env;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;

#[derive(Clone)]
enum Expr {
  Int(i64),
  Str(String)
}

// XXX this should probably be split up. The VM has the instructions, a "Thread" has locals, stack, and arguments.
struct VM<'a> {
  instructions: Vec<&'a str>,
  locals: HashMap<&'a str, Expr>,
  labels: HashMap<&'a str, usize>,
}

fn parse_labels<'a>(instructions: &Vec<&'a str>) -> HashMap<&'a str, usize> {
  let mut map = HashMap::new();
  for (i, instr) in instructions.iter().enumerate() {
    if instr.starts_with("$label") {
      match instr.split(' ').nth(1) {
        Some(label) => map.insert(label, i),
        None => panic!("VM error: missing label"),
      };
    }
  };
  map
}

impl<'a> VM<'a> {
  pub fn new(instructions: Vec<&'a str>) -> VM {
    VM {
      labels: parse_labels(&instructions),
      instructions: instructions,
      locals: HashMap::new(),
    }
  }

  pub fn run(&mut self) {
    let ip = self.labels.get("main").unwrap_or(&0).clone();
    self.run_call(ip, &mut Vec::new());
  }

  // TODO locals
  pub fn run_call(&mut self, start_ip: usize, stack: &mut Vec<Expr>) -> Option<Expr> {
    let mut ip = start_ip;
    let len = self.instructions.len();

    let mut carry = false; // comparison carry

    let mut arguments = Vec::new();

    while ip < len {
      let instr = self.instructions[ip];
      ip += 1;
      if instr.starts_with("#") || instr.trim() == "" {
        continue;
      }
      match instr.split(" ").collect::<Vec<_>>().as_slice() {
        ["$label", _] => (),
        ["push", "int", n] => stack.push(Expr::Int(n.parse::<i64>().unwrap())),
        ["push", "str", n] => stack.push(Expr::Str(String::from(n))),
        ["push", "arg"] => arguments.push(stack.pop().expect("Nothing on the stack to push to the arguments")),

        ["dup"] => {
          let expr = stack.pop().expect("Nothing on the stack to pop");
          stack.push(expr.clone());
          stack.push(expr);
        },

        // convert to string
        ["convert", "int", "str"] => if let Some(&Expr::Int(arg)) = stack.last() {
          stack.push(Expr::Str(arg.to_string()));
        } else {
          panic!("VM error: cannot convert int to string");
        },

        ["convert", from, to] => panic!("VM error: cannot convert from {} to {}", from, to),

        // how = "always" | "carry"
        ["jump", how, n] => {
          let new_ip = if n.starts_with("$") {
            self.labels.get(n.trim_matches('$')).expect("VM error: no such label").clone()
          } else {
            n.parse::<usize>().unwrap()
          };

          if new_ip < len {
            // only check carry if we are conditionally jumping
            if how == "always" || (how == "carry" && carry) {
              ip = new_ip;
            }
          } else {
            panic!("VM error: trying to jump past end of bytecode ({} > {})", new_ip, len);
          }
        },

        ["carry", "set", val] => carry = val == "true",
        ["carry", "invert"] => carry = !carry,

        ["call", "primitive", "say"] => if let Some(Expr::Str(arg)) = stack.pop() {
          println!("hey {}", arg);
        } else {
          panic!("VM error: incorrect arguments to `say`");
        },

        // TODO resolve name in scope, check type, apply
        ["call", name] => {
          let new_ip = self.labels.get(name).expect("VM error: no such label").clone();
          let ret = self.run_call(new_ip, &mut arguments);
          arguments = Vec::new();
          match ret {
            Some(val) => stack.push(val),
            None => (),
          };
        },

        ["ret"] => {
          return None;
        },

        ["ret", "val"] => {
          if stack.len() != 1 {
            panic!("VM error: trying to return a value but the stack size is not 1 ({})", stack.len());
          }
          return stack.pop();
        },

        // NOTE: it's stack[*-1] OP stack[*-2]
        // which means if have stack=[1, 2]
        // you'll have 2 OP 1
        /// XXX this should JUST be a call!
        ["math", op] => if let
            (Some(Expr::Int(arg1)), Some(Expr::Int(arg2)))
            =
            (stack.pop(), stack.pop()) {
          let value = match op {
            "+" => arg1 + arg2,
            "-" => arg1 + arg2,
            //"/" => TODO. this should have very specific rules...
            "*" => arg1 * arg2,
            _ => panic!("VM error: unrecognized `op` operator: {}", op),
          };
          stack.push(Expr::Int(value));
        } else {
          panic!("VM error: not enough arguments to `math`");
        },

        // same note about "2 OP 1"
        // XXX same note about how this should be a `call`
        ["cmp", op] => if let
            (Some(Expr::Int(arg1)), Some(Expr::Int(arg2)))
            =
            (stack.pop(), stack.pop()) {
          carry = match op {
            "<" => arg1 < arg2,
            ">" => arg1 > arg2,
            "<=" => arg1 <= arg2,
            ">=" => arg1 >= arg2,
            "=" => arg1 == arg2,
            _ => panic!("VM error: unrecognized `cmp` operator: {}", op),
          };
        } else {
          panic!("VM error: not enough arguments to `cmp`");
        },

        // TODO this *shouldn't* be an identifier
        //      the backend should transform SSAF to tons of load/store
        //      (probably)
        //      (see backend#1)
        ["local", "load", name] => match self.locals.get(name) {
          Some(expr) => stack.push(expr.clone()),
          None       => panic!("VM error: unknown local variable {}", name),
        },

        ["local", "store", name] => if let Some(arg) = stack.pop() {
          // XXX check
          let _ = self.locals.insert(name, arg);
        } else {
          panic!("VM error: not enough arguments for `local store`");
        },

        [instr, ..] => panic!("VM error: no such instruction {}", instr),
        [..] => panic!("VM error: Unrecognized instruction!"),
      }
    }

    None
  }
}

fn main() {
	if let Some(path) = env::args().nth(1) {
		println!("Loading file {}", path);

    match File::open(&path) {
      Ok(mut file) => {
        let mut content = String::new();
        if let Ok(_) = file.read_to_string(&mut content) {
          // remove trailing newlines, split by line
          let instructions = content.trim_matches('\n').split('\n').collect();
          let mut vm = VM::new(instructions);
          vm.run();
        }
      },
      Err(err) => {
        panic!("Can't read file {} (error: {})", path, err);
      }
    }
	} else {
    panic!("Please give a file argument");
  }

  //     let path = Path::new("chry.fa");
  // let mut file = BufferedReader::new(File::open(&path));
  // for line in file.lines() {
  //  print!("{}", line.unwrap());
  //      }
}
<|endoftext|>


<commit_before>extern crate gcc;
extern crate syntex_syntax as syntax;

use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::iter;
use std::path::{Path, PathBuf};

use syntax::ast;
use syntax::diagnostic::SpanHandler;
use syntax::parse::token::InternedString;
use syntax::attr::{self, ReprAttr};
use syntax::parse::{self, ParseSess};
use syntax::visit::{self, Visitor};

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

struct TestGenerator<'a> {
    target: String,
    rust: Box<Write>,
    c: Box<Write>,
    sh: &'a SpanHandler,
    structs: HashSet<String>,
}

struct StructFinder {
    structs: HashSet<String>,
}

impl<'a> TestGenerator<'a> {
    fn defines(&self) -> Vec<&'static str> {
        let mut ret = Vec::new();
        if self.target.contains("unknown-linux") {
            ret.push("_GNU_SOURCE");
        }
        return ret
    }

    fn headers(&self) -> Vec<&'static str> {
        let mut base = vec![
            "errno.h",
            "fcntl.h",
            "glob.h",
            "ifaddrs.h",
            "limits.h",
            "net/if.h",
            "netdb.h",
            "netinet/in.h",
            "netinet/ip.h",
            "netinet/tcp.h",
            "pthread.h",
            "signal.h",
            "stdalign.h",
            "stddef.h",
            "stdint.h",
            "stdio.h",
            "stdlib.h",
            "sys/mman.h",
            "sys/resource.h",
            "sys/socket.h",
            "sys/stat.h",
            "sys/time.h",
            "sys/types.h",
            "sys/un.h",
            "time.h",
            "unistd.h",
            "utime.h",
            "wchar.h",
        ];

        if self.target.contains("apple-darwin") {
            base.push("mach/mach_time.h");
        }
        if self.target.contains("unknown-linux") {
            base.push("linux/if_packet.h");
            base.push("net/ethernet.h");
        }

        return base
    }

    fn rust2c(&self, ty: &str) -> String {
        match ty {
            t if t.starts_with("c_") => {
                match &ty[2..].replace("long", " long")[..] {
                    s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
                    "short" => format!("short"),
                    s if s.starts_with("s") => format!("signed {}", &s[1..]),
                    s => s.to_string(),
                }
            }
            "ip6_mreq" => "struct ipv6_mreq".to_string(),
            "glob_t" => "glob_t".to_string(),
            t if t.starts_with("pthread") => t.to_string(),

            t if self.structs.contains(t) => format!("struct {}", t),

            t => t.to_string(),
        }
    }

    fn rust2cfield(&self, struct_: &str, field: &str) -> String {
        match field {
            s if s.ends_with("_nsec") && struct_ == "stat" => {
                if self.target.contains("apple-darwin") {
                    s.replace("_nsec", "spec.tv_nsec")
                } else {
                    s.replace("e_nsec", ".tv_nsec")
                }
            }
            s => s.to_string(),
        }
    }

    fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
        let mut ret = Vec::new();
        let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
            ("x86_64", "64")
        } else if self.target.starts_with("i686") {
            ("x86", "32")
        } else {
            panic!("unknown arch/pointer width: {}", self.target)
        };
        let (os, family, env) = if self.target.contains("unknown-linux") {
            ("linux", "unix", "gnu")
        } else if self.target.contains("apple-darwin") {
            ("macos", "unix", "")
        } else {
            panic!("unknown os/family width: {}", self.target)
        };

        ret.push((family, None));
        ret.push(("target_os", Some(os)));
        ret.push(("target_family", Some(family)));
        ret.push(("target_arch", Some(arch)));
        // skip endianness
        ret.push(("target_pointer_width", Some(target_pointer_width)));
        ret.push(("target_env", Some(env)));

        return ret
    }
}

fn main() {
    // Prep the test generator
    let target = t!(env::var("TARGET"));
    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
    let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
    let sess = ParseSess::new();
    let mut tg = TestGenerator {
        target: target,
        rust: Box::new(rust_out),
        c: Box::new(c_out),
        sh: &sess.span_diagnostic,
        structs: HashSet::new(),
    };

    // Parse the libc crate
    let src = Path::new("../src/lib.rs");
    let cfg = Vec::new();
    let mut krate = parse::parse_crate_from_file(src, cfg, &sess);

    // Strip the crate down to just what's configured for our target
    for (k, v) in tg.cfg_list() {
        let s = InternedString::new;
        krate.config.push(match v {
            Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
            None => attr::mk_word_item(s(k)),
        });
    }
    let mut gated_cfgs = Vec::new();
    let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
                                                         krate,
                                                         &mut gated_cfgs);

    // Probe the crate to find all structs (used to convert type names to names
    // in C).
    let mut structs = StructFinder {
        structs: HashSet::new(),
    };
    visit::walk_crate(&mut structs, &krate);
    tg.structs = structs.structs;

    // Prep the C file by emitting header stuff
    for define in tg.defines() {
        t!(writeln!(tg.c, "#define {}", define));
    }
    for header in tg.headers() {
        t!(writeln!(tg.c, "#include <{}>", header));
    }

    // Walk the crate, emitting test cases for everything found
    visit::walk_crate(&mut tg, &krate);
    drop(tg);

    // Compile our C shim to be linked into tests
    gcc::Config::new()
                .file(out.join("all.c"))
                .flag("-Wall")
                .flag("-Wextra")
                .flag("-Werror")
                .compile("liball.a");
}

impl<'a> TestGenerator<'a> {
    fn test_type(&mut self, ty: &str) {
        match ty {
            "sighandler_t" => return,
            _ => {}
        }
        let c = self.rust2c(ty);
        self.test_size_align(ty, &c);
    }

    fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
        let cty = self.rust2c(ty);
        self.test_size_align(ty, &cty);

        t!(writeln!(self.rust, r#"
            #[test]
            fn field_offset_size_{ty}() {{
        "#, ty = ty));
        for field in s.fields.iter() {
            let name = match field.node.kind {
                ast::NamedField(name, ast::Public) => name,
                ast::NamedField(_, ast::Inherited) => continue,
                ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
            };

            let cfield = self.rust2cfield(ty, &name.to_string());

            t!(writeln!(self.c, r#"
                uint64_t __test_offset_{ty}_{rust_field}() {{
                    return offsetof({cty}, {c_field});
                }}
                uint64_t __test_size_{ty}_{rust_field}() {{
                    {cty}* foo = NULL;
                    return sizeof(foo->{c_field});
                }}
            "#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
            t!(writeln!(self.rust, r#"
                extern {{
                    fn __test_offset_{ty}_{field}() -> u64;
                    fn __test_size_{ty}_{field}() -> u64;
                }}
                unsafe {{
                    let foo = 0 as *const {ty};
                    same(offset_of!({ty}, {field}),
                         __test_offset_{ty}_{field}(),
                         "field offset {field} of {ty}");
                    same(mem::size_of_val(&(*foo).{field}) as u64,
                         __test_size_{ty}_{field}(),
                         "field size {field} of {ty}");
                }}
            "#, ty = ty, field = name));
        }
        t!(writeln!(self.rust, r#"
            }}
        "#));
    }

    fn test_size_align(&mut self, rust: &str, c: &str) {
        t!(writeln!(self.c, r#"
            uint64_t __test_size_{ty}() {{ return sizeof({cty}); }}
            uint64_t __test_align_{ty}() {{ return alignof({cty}); }}
        "#, ty = rust, cty = c));
        t!(writeln!(self.rust, r#"
            #[test]
            fn size_align_{ty}() {{
                extern {{
                    fn __test_size_{ty}() -> u64;
                    fn __test_align_{ty}() -> u64;
                }}
                unsafe {{
                    same(mem::size_of::<{ty}>() as u64,
                         __test_size_{ty}(), "size");
                    same(align::<{ty}>() as u64,
                         __test_align_{ty}(), "align");
                }}
            }}
        "#, ty = rust));
    }

    fn test_const(&mut self, name: &str, rust_ty: &str) {
        let cty = self.rust2c(&rust_ty.replace("*mut ", "")
                                      .replace("*const ", ""));
        let ptrs = rust_ty.matches("*").count();
        let cty = format!("{}{}", cty,
                          iter::repeat("*").take(ptrs).collect::<String>());
        let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
        t!(writeln!(self.c, r#"
            int __test_const_{name}({cty} *out) {{
                int ret = 0;
                #if defined({name})
                    *out = {cast}({name});
                    ret = 1;
                #endif
                return ret;
            }}
        "#, name = name, cast = cast, cty = cty));
        t!(writeln!(self.rust, r#"
            #[test]
            fn const_{name}() {{
                extern {{
                    fn __test_const_{name}(out: *mut {ty}) -> c_int;
                }}
                unsafe {{
                    let mut o = mem::zeroed();
                    if __test_const_{name}(&mut o) == 0 {{
                        panic!("not defined");
                    }} else {{
                        same({name}, o, "value");
                    }}
                }}
            }}
        "#, ty = rust_ty, name = name));
    }

    fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
         assert!(generics.lifetimes.len() == 0);
         assert!(generics.ty_params.len() == 0);
         assert!(generics.where_clause.predicates.len() == 0);
    }

    fn ty2name(&self, ty: &ast::Ty) -> String {
        match ty.node {
            ast::TyPath(_, ref path) => {
                path.segments.last().unwrap().identifier.to_string()
            }
            ast::TyPtr(ref t) => {
                format!("*{} {}", match t.mutbl {
                    ast::MutImmutable => "const",
                    ast::MutMutable => "mut",
                }, self.ty2name(&t.ty))
            }
            _ => panic!("unknown ty {:?}", ty),
        }
    }
}

impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
     fn visit_item(&mut self, i: &'v ast::Item) {
         match i.node {
             ast::ItemTy(_, ref generics) => {
                 self.assert_no_generics(i, generics);
                 self.test_type(&i.ident.to_string());
             }

             ast::ItemStruct(ref s, ref generics) => {
                 self.assert_no_generics(i, generics);
                 let is_c = i.attrs.iter().any(|a| {
                    attr::find_repr_attrs(self.sh, a).iter().any(|a| {
                        *a == ReprAttr::ReprExtern
                    })
                 });
                 if !is_c {
                     panic!("{} is not marked #[repr(C)]", i.ident);
                 }
                 self.test_struct(&i.ident.to_string(), s);
             }

             ast::ItemConst(ref ty, _) => {
                 let ty = self.ty2name(ty);
                 self.test_const(&i.ident.to_string(), &ty);
             }

             _ => {}
         }
         visit::walk_item(self, i)
     }
}

impl<'v> Visitor<'v> for StructFinder {
     fn visit_item(&mut self, i: &'v ast::Item) {
         match i.node {
             ast::ItemStruct(..) => {
                 self.structs.insert(i.ident.to_string());
             }

             _ => {}
         }
         visit::walk_item(self, i)
     }
}
<commit_msg>Windows MSVC support<commit_after>extern crate gcc;
extern crate syntex_syntax as syntax;

use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
use std::iter;
use std::path::{Path, PathBuf};

use syntax::ast;
use syntax::diagnostic::SpanHandler;
use syntax::parse::token::InternedString;
use syntax::attr::{self, ReprAttr};
use syntax::parse::{self, ParseSess};
use syntax::visit::{self, Visitor};

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

struct TestGenerator<'a> {
    target: String,
    rust: Box<Write>,
    c: Box<Write>,
    sh: &'a SpanHandler,
    structs: HashSet<String>,
}

struct StructFinder {
    structs: HashSet<String>,
}

impl<'a> TestGenerator<'a> {
    fn defines(&self) -> Vec<&'static str> {
        let mut ret = Vec::new();
        if self.target.contains("unknown-linux") {
            ret.push("_GNU_SOURCE");
        }
        if self.target.contains("msvc") {
            ret.push("alignof __alignof");
        }
        return ret
    }

    fn headers(&self) -> Vec<&'static str> {
        let mut base = Vec::new();

        if self.target.contains("windows") {
            base.push("windows.h");
        }

        base.extend(&[
            "errno.h",
            "fcntl.h",
            "limits.h",
            "stddef.h",
            "stdint.h",
            "stdio.h",
            "stdlib.h",
            "sys/stat.h",
            "sys/types.h",
            "time.h",
            "wchar.h",
        ]);

        if self.target.contains("apple-darwin") {
            base.push("mach/mach_time.h");
        }

        if self.target.contains("unknown-linux") {
            base.push("linux/if_packet.h");
            base.push("net/ethernet.h");
        }

        if self.target.contains("windows") {
            base.push("ws2tcpip.h");
        } else {
            base.push("glob.h");
            base.push("ifaddrs.h");
            base.push("net/if.h");
            base.push("netdb.h");
            base.push("netinet/in.h");
            base.push("netinet/ip.h");
            base.push("netinet/tcp.h");
            base.push("pthread.h");
            base.push("signal.h");
            base.push("stdalign.h");
            base.push("sys/mman.h");
            base.push("sys/resource.h");
            base.push("sys/socket.h");
            base.push("sys/time.h");
            base.push("sys/un.h");
            base.push("utime.h");
            base.push("unistd.h");
        }

        return base
    }

    fn rust2c(&self, ty: &str) -> String {
        match ty {
            t if t.starts_with("c_") => {
                match &ty[2..].replace("long", " long")[..] {
                    s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
                    "short" => format!("short"),
                    s if s.starts_with("s") => format!("signed {}", &s[1..]),
                    s => s.to_string(),
                }
            }
            "ip6_mreq" => "struct ipv6_mreq".to_string(),
            "glob_t" => "glob_t".to_string(),
            t if t.starts_with("pthread") => t.to_string(),

            t if self.structs.contains(t) => format!("struct {}", t),

            t => t.to_string(),
        }
    }

    fn rust2cfield(&self, struct_: &str, field: &str) -> String {
        match field {
            s if s.ends_with("_nsec") && struct_ == "stat" => {
                if self.target.contains("apple-darwin") {
                    s.replace("_nsec", "spec.tv_nsec")
                } else {
                    s.replace("e_nsec", ".tv_nsec")
                }
            }
            s => s.to_string(),
        }
    }

    fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
        let mut ret = Vec::new();
        let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
            ("x86_64", "64")
        } else if self.target.starts_with("i686") {
            ("x86", "32")
        } else {
            panic!("unknown arch/pointer width: {}", self.target)
        };
        let (os, family, env) = if self.target.contains("unknown-linux") {
            ("linux", "unix", "gnu")
        } else if self.target.contains("apple-darwin") {
            ("macos", "unix", "")
        } else if self.target.contains("windows-msvc") {
            ("windows", "windows", "msvc")
        } else if self.target.contains("windows-gnu") {
            ("windows", "windows", "gnu")
        } else {
            panic!("unknown os/family width: {}", self.target)
        };

        ret.push((family, None));
        ret.push(("target_os", Some(os)));
        ret.push(("target_family", Some(family)));
        ret.push(("target_arch", Some(arch)));
        // skip endianness
        ret.push(("target_pointer_width", Some(target_pointer_width)));
        ret.push(("target_env", Some(env)));

        return ret
    }
}

fn main() {
    // Prep the test generator
    let target = t!(env::var("TARGET"));
    let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
    let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
    let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
    let sess = ParseSess::new();
    let mut tg = TestGenerator {
        target: target,
        rust: Box::new(rust_out),
        c: Box::new(c_out),
        sh: &sess.span_diagnostic,
        structs: HashSet::new(),
    };

    // Parse the libc crate
    let src = Path::new("../src/lib.rs");
    let cfg = Vec::new();
    let mut krate = parse::parse_crate_from_file(src, cfg, &sess);

    // Strip the crate down to just what's configured for our target
    for (k, v) in tg.cfg_list() {
        let s = InternedString::new;
        krate.config.push(match v {
            Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
            None => attr::mk_word_item(s(k)),
        });
    }
    let mut gated_cfgs = Vec::new();
    let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
                                                         krate,
                                                         &mut gated_cfgs);

    // Probe the crate to find all structs (used to convert type names to names
    // in C).
    let mut structs = StructFinder {
        structs: HashSet::new(),
    };
    visit::walk_crate(&mut structs, &krate);
    tg.structs = structs.structs;

    // Prep the C file by emitting header stuff
    for define in tg.defines() {
        t!(writeln!(tg.c, "#define {}", define));
    }
    for header in tg.headers() {
        t!(writeln!(tg.c, "#include <{}>", header));
    }

    // Walk the crate, emitting test cases for everything found
    visit::walk_crate(&mut tg, &krate);

    // Compile our C shim to be linked into tests
    let mut cfg = gcc::Config::new();
    cfg.file(out.join("all.c"));

    if tg.target.contains("msvc") {
        cfg.flag("/W3").flag("/Wall").flag("/WX");
    } else {
        cfg.flag("-Wall").flag("-Wextra").flag("-Werror");
    }

    drop(tg);
    cfg.compile("liball.a");
}

impl<'a> TestGenerator<'a> {
    fn test_type(&mut self, ty: &str) {
        match ty {
            "sighandler_t" => return,
            _ => {}
        }
        let c = self.rust2c(ty);
        self.test_size_align(ty, &c);
    }

    fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
        let cty = self.rust2c(ty);
        self.test_size_align(ty, &cty);

        t!(writeln!(self.rust, r#"
            #[test]
            fn field_offset_size_{ty}() {{
        "#, ty = ty));
        for field in s.fields.iter() {
            let name = match field.node.kind {
                ast::NamedField(name, ast::Public) => name,
                ast::NamedField(_, ast::Inherited) => continue,
                ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
            };

            let cfield = self.rust2cfield(ty, &name.to_string());

            t!(writeln!(self.c, r#"
                uint64_t __test_offset_{ty}_{rust_field}() {{
                    return offsetof({cty}, {c_field});
                }}
                uint64_t __test_size_{ty}_{rust_field}() {{
                    {cty}* foo = NULL;
                    return sizeof(foo->{c_field});
                }}
            "#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
            t!(writeln!(self.rust, r#"
                extern {{
                    fn __test_offset_{ty}_{field}() -> u64;
                    fn __test_size_{ty}_{field}() -> u64;
                }}
                unsafe {{
                    let foo = 0 as *const {ty};
                    same(offset_of!({ty}, {field}),
                         __test_offset_{ty}_{field}(),
                         "field offset {field} of {ty}");
                    same(mem::size_of_val(&(*foo).{field}) as u64,
                         __test_size_{ty}_{field}(),
                         "field size {field} of {ty}");
                }}
            "#, ty = ty, field = name));
        }
        t!(writeln!(self.rust, r#"
            }}
        "#));
    }

    fn test_size_align(&mut self, rust: &str, c: &str) {
        t!(writeln!(self.c, r#"
            uint64_t __test_size_{ty}() {{ return sizeof({cty}); }}
            uint64_t __test_align_{ty}() {{ return alignof({cty}); }}
        "#, ty = rust, cty = c));
        t!(writeln!(self.rust, r#"
            #[test]
            fn size_align_{ty}() {{
                extern {{
                    fn __test_size_{ty}() -> u64;
                    fn __test_align_{ty}() -> u64;
                }}
                unsafe {{
                    same(mem::size_of::<{ty}>() as u64,
                         __test_size_{ty}(), "size");
                    same(align::<{ty}>() as u64,
                         __test_align_{ty}(), "align");
                }}
            }}
        "#, ty = rust));
    }

    fn test_const(&mut self, name: &str, rust_ty: &str) {
        let cty = self.rust2c(&rust_ty.replace("*mut ", "")
                                      .replace("*const ", ""));
        let ptrs = rust_ty.matches("*").count();
        let cty = format!("{}{}", cty,
                          iter::repeat("*").take(ptrs).collect::<String>());
        let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
        t!(writeln!(self.c, r#"
            int __test_const_{name}({cty} *out) {{
                int ret = 0;
                #if defined({name})
                    *out = {cast}({name});
                    ret = 1;
                #endif
                return ret;
            }}
        "#, name = name, cast = cast, cty = cty));
        t!(writeln!(self.rust, r#"
            #[test]
            fn const_{name}() {{
                extern {{
                    fn __test_const_{name}(out: *mut {ty}) -> c_int;
                }}
                unsafe {{
                    let mut o = mem::zeroed();
                    if __test_const_{name}(&mut o) == 0 {{
                        panic!("not defined");
                    }} else {{
                        same({name}, o, "value");
                    }}
                }}
            }}
        "#, ty = rust_ty, name = name));
    }

    fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
         assert!(generics.lifetimes.len() == 0);
         assert!(generics.ty_params.len() == 0);
         assert!(generics.where_clause.predicates.len() == 0);
    }

    fn ty2name(&self, ty: &ast::Ty) -> String {
        match ty.node {
            ast::TyPath(_, ref path) => {
                path.segments.last().unwrap().identifier.to_string()
            }
            ast::TyPtr(ref t) => {
                format!("*{} {}", match t.mutbl {
                    ast::MutImmutable => "const",
                    ast::MutMutable => "mut",
                }, self.ty2name(&t.ty))
            }
            _ => panic!("unknown ty {:?}", ty),
        }
    }
}

impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
     fn visit_item(&mut self, i: &'v ast::Item) {
         match i.node {
             ast::ItemTy(_, ref generics) => {
                 self.assert_no_generics(i, generics);
                 self.test_type(&i.ident.to_string());
             }

             ast::ItemStruct(ref s, ref generics) => {
                 self.assert_no_generics(i, generics);
                 let is_c = i.attrs.iter().any(|a| {
                    attr::find_repr_attrs(self.sh, a).iter().any(|a| {
                        *a == ReprAttr::ReprExtern
                    })
                 });
                 if !is_c {
                     panic!("{} is not marked #[repr(C)]", i.ident);
                 }
                 self.test_struct(&i.ident.to_string(), s);
             }

             ast::ItemConst(ref ty, _) => {
                 let ty = self.ty2name(ty);
                 self.test_const(&i.ident.to_string(), &ty);
             }

             _ => {}
         }
         visit::walk_item(self, i)
     }
}

impl<'v> Visitor<'v> for StructFinder {
     fn visit_item(&mut self, i: &'v ast::Item) {
         match i.node {
             ast::ItemStruct(..) => {
                 self.structs.insert(i.ident.to_string());
             }

             _ => {}
         }
         visit::walk_item(self, i)
     }
}
<|endoftext|>


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

use std::path::PathBuf;

/// Interprets given bytes as an operating system path.
///
/// On Linux, the path is constructed from bytes as is since system paths can
/// be made of arbitrary sequence of bytes.
///
/// On Windows, the behaviour is unspecified (for now).
///
/// ```
/// use std::ffi::OsStr;
///
/// let path = rrg_proto::path::from_bytes(b"foo/bar/baz".to_vec());
///
/// let mut components = path.components()
///     .map(|component| component.as_os_str());
///
/// assert_eq!(components.next(), Some(OsStr::new("foo")));
/// assert_eq!(components.next(), Some(OsStr::new("bar")));
/// assert_eq!(components.next(), Some(OsStr::new("baz")));
/// assert_eq!(components.next(), None);
/// ```
pub fn from_bytes(bytes: Vec<u8>) -> PathBuf {
    from_bytes_impl(bytes)
}

pub fn to_bytes(path: PathBuf) -> Vec<u8> {
    to_bytes_impl(path)
}

#[cfg(target_family = "unix")]
fn from_bytes_impl(bytes: Vec<u8>) -> PathBuf {
    use std::os::unix::ffi::OsStringExt as _;
    std::ffi::OsString::from_vec(bytes).into()
}

#[cfg(target_family = "windows")]
fn from_bytes_impl(bytes: Vec<u8>) -> PathBuf {
    // TODO: This is just a quick hack that treats UTF-8-encoded strings as
    // UTF-16. This works for trivial cases but should be reworked once the GRR
    // protocol for paths is defined.
    let bytes_u16 = bytes.iter().map(|byte| *byte as u16).collect::<Vec<_>>();

    use std::os::windows::ffi::OsStringExt as _;
    std::ffi::OsString::from_wide(&bytes_u16).into()
}

#[cfg(target_family = "unix")]
fn to_bytes_impl(path: PathBuf) -> Vec<u8> {
    use std::os::unix::ffi::OsStringExt as _;
    std::ffi::OsString::from(path).into_vec()
}

#[cfg(target_family = "windows")]
fn to_bytes_impl(path: PathBuf) -> Vec<u8> {
    let string = std::ffi::OsString::from(path);

    // TODO: See explanation in `from_bytes_impl` on Windows.
    use std::os::windows::ffi::OsStrExt as _;
    string.as_os_str().encode_wide().map(|byte| byte as u8).collect()
}
<commit_msg>Write documentation for path serialization.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use std::path::PathBuf;

/// Interprets given bytes as an operating system path.
///
/// On Linux, the path is constructed from bytes as is since system paths can
/// be made of arbitrary sequence of bytes.
///
/// On Windows, the behaviour is unspecified (for now).
///
/// ```
/// use std::ffi::OsStr;
///
/// let path = rrg_proto::path::from_bytes(b"foo/bar/baz".to_vec());
///
/// let mut components = path.components()
///     .map(|component| component.as_os_str());
///
/// assert_eq!(components.next(), Some(OsStr::new("foo")));
/// assert_eq!(components.next(), Some(OsStr::new("bar")));
/// assert_eq!(components.next(), Some(OsStr::new("baz")));
/// assert_eq!(components.next(), None);
/// ```
pub fn from_bytes(bytes: Vec<u8>) -> PathBuf {
    from_bytes_impl(bytes)
}

/// Serializes given path to a byte sequence.
///
/// On Linux, the path is emitted as is since system paths can consist of
/// arbitrary bytes.
///
/// On Windows, the behaviour is unspecified (for now).
///
/// ```
/// use std::path::PathBuf;
///
/// let path = PathBuf::from("foo/bar/baz");
/// assert_eq!(rrg_proto::path::to_bytes(path), b"foo/bar/baz");
/// ```
pub fn to_bytes(path: PathBuf) -> Vec<u8> {
    to_bytes_impl(path)
}

#[cfg(target_family = "unix")]
fn from_bytes_impl(bytes: Vec<u8>) -> PathBuf {
    use std::os::unix::ffi::OsStringExt as _;
    std::ffi::OsString::from_vec(bytes).into()
}

#[cfg(target_family = "windows")]
fn from_bytes_impl(bytes: Vec<u8>) -> PathBuf {
    // TODO: This is just a quick hack that treats UTF-8-encoded strings as
    // UTF-16. This works for trivial cases but should be reworked once the GRR
    // protocol for paths is defined.
    let bytes_u16 = bytes.iter().map(|byte| *byte as u16).collect::<Vec<_>>();

    use std::os::windows::ffi::OsStringExt as _;
    std::ffi::OsString::from_wide(&bytes_u16).into()
}

#[cfg(target_family = "unix")]
fn to_bytes_impl(path: PathBuf) -> Vec<u8> {
    use std::os::unix::ffi::OsStringExt as _;
    std::ffi::OsString::from(path).into_vec()
}

#[cfg(target_family = "windows")]
fn to_bytes_impl(path: PathBuf) -> Vec<u8> {
    let string = std::ffi::OsString::from(path);

    // TODO: See explanation in `from_bytes_impl` on Windows.
    use std::os::windows::ffi::OsStrExt as _;
    string.as_os_str().encode_wide().map(|byte| byte as u8).collect()
}
<|endoftext|>