<commit_before>// FIXME Remove once not dev anymore
#![allow(dead_code, unused_imports)]
#[macro_use]
extern crate slog;
extern crate slog_term;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate hyper_tls;
extern crate time;

use std::env;
use std::io::{self, Write};

use serde::{Serialize, Serializer, Deserialize, Deserializer};
use futures::{Future, Stream};
use futures::future;
use slog::*;
use hyper::{Client, Uri, Method, Chunk, Error, StatusCode};
use hyper::header::{Authorization, Accept, UserAgent, qitem};
use hyper::client::{Request, Response, FutureResponse};
use hyper_tls::HttpsConnector;
use tokio_core::reactor::{Core, Handle};
mod utils;

///
/// 'Main' struct which have relevant parts which are use as core elements of the application
///
struct Main {
    core: Core,
    endpoint: HnNews,
    client: Client<hyper_tls::HttpsConnector>,
    logger: Logger,
}

fn create_main() -> Main {
    let logger = create_loggers();
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let client = configure_client(&handle);
    let endpoint = HnNews::build_default();
    let mut main = Main {
        core: core,
        endpoint: endpoint,
        client: client,
        logger: logger,
    };
    main
}
fn configure_client(handle: &Handle) -> Client<hyper_tls::HttpsConnector> {
    Client::configure()
        // Does not check the validity of certificate
        .connector(HttpsConnector::new(4, &handle))
        .build(&handle)
}

fn main() {
    let mut main = create_main();
    info!(&main.logger, "Application started");
    let response = get_top_story_ids(&mut main);
    println!("{}", response.unwrap());
}

fn get_comments_for_a_story(main: &mut Main, story_id: i32) {}

fn get_top_story_ids(main: &mut Main) -> Result<String, hyper::Error> {
    let logger = &main.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &main.endpoint;
    let client = &main.client;
    let work = request_top_story_ids(&client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger, &endpoint.get_top_stories_path(), &res.status());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| String::from_utf8(chunks).unwrap());
    let result = main.core.run(work);
    result
}

fn log_response_status(logger: &Logger, url: &str, status: &StatusCode) {
    info!(logger,
          format!("Request to {} finished with status {}", url, status));
}

fn common_headers(req: &mut Request) {
    req.headers_mut().set(UserAgent::new("hyper"));
}
fn consume_request(client: &Client<hyper_tls::HttpsConnector>, request: Request) -> FutureResponse {
    client.request(request)
}

fn create_loggers() -> Logger {
    let drain = slog_term::streamer().build().fuse();
    let root_logger = Logger::root(drain, o!());
    return root_logger;
}

fn request_top_story_ids(client: &Client<hyper_tls::HttpsConnector>,
                         endpoints: &HnNews)
                         -> FutureResponse {
    let url = utils::parse_url_from_str(&endpoints.get_top_stories_path());
    let mut request = Request::new(Method::Get, url);
    common_headers(&mut request);
    client.request(request)
}

struct HnNews {
    base_url: String,
    top_news_suffix: String,
    item_suffix: String,
    max_item_suffix: String,
    json_suffix: String,
}

impl HnNews {
    pub fn build_default() -> HnNews {
        let e = HnNews {
            base_url: String::from("https://hacker-news.firebaseio.com/v0/"),
            top_news_suffix: String::from("topstories"),
            item_suffix: String::from("item/"),
            max_item_suffix: String::from("maxitem"),
            json_suffix: String::from(".json"),
        };
        e
    }

    pub fn get_top_stories_path(&self) -> String {
        utils::combine_strings(vec![&self.base_url, &self.top_news_suffix, &self.json_suffix])
    }
    pub fn get_max_item_path(&self) -> String {
        utils::combine_strings(vec![&self.base_url, &self.max_item_suffix, &self.json_suffix])
    }
    pub fn get_item_path(&self, id: &str) -> String {
        utils::combine_strings(vec![&self.base_url, &self.item_suffix, id, &self.json_suffix])
    }
}

#[derive(Serialize)]
struct HnTopStories {
    values: Vec<i32>,
}

impl Deserialize for HnTopStories {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        Deserialize::deserialize(deserializer).map(|arr: Vec<i32>| HnTopStories { values: arr })
    }
}

#[derive(Serialize, Deserialize)]
struct HnItem {
    by: String,
    descendants: i32,
    id: i32,
    kids: Vec<i32>,
    title: String,
    score: i32,
    time: f64,
    #[serde(rename(deserialize = "type"))]
    type_str: String,
    url: String,
}

#[derive(Serialize, Deserialize)]
struct HnUser {
    about: String,
    created: f64,
    id: String,
    karma: i32,
    submitted: Vec<i32>,
}

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

    #[test]
    fn hn_item_serde_test() {
        use std::fs::File;
        use std::io::prelude::*;
        let mut contents = String::new();
        File::open("res/test/item.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnItem = serde_json::from_str(&contents).unwrap();
        assert_eq!(71, deserialized.descendants);
        assert_eq!("dhouston", deserialized.by);
        assert_eq!(8863, deserialized.id);
        assert_eq!(111, deserialized.score);
        assert_eq!(1175714200.0f64, deserialized.time);
        assert_eq!("My YC app: Dropbox - Throw away your USB drive",
                   deserialized.title);
        assert_eq!("story", deserialized.type_str);
        assert_eq!("http://www.getdropbox.com/u/2/screencast.html",
                   deserialized.url);
    }
    #[test]
    fn hn_top_stories_serde_test() {
        use std::fs::File;
        use std::io::prelude::*;
        let mut contents = String::new();
        File::open("res/test/top-stories.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnTopStories = serde_json::from_str(&contents).unwrap();
        assert!(deserialized.values.len() > 3);
    }
    #[test]
    fn hn_user_serde_test() {
        use std::fs::File;
        use std::io::prelude::*;
        let mut contents = String::new();
        File::open("res/test/user.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnUser = serde_json::from_str(&contents).unwrap();
        assert_eq!("This is a test", deserialized.about);
        assert_eq!(1173923446.0f64, deserialized.created);
        assert_eq!("jl", deserialized.id);
        assert_eq!(3496, deserialized.karma);
        assert!(deserialized.submitted.len() > 3);
    }

}
<commit_msg>couple methods to request item and test<commit_after>// FIXME Remove once not dev anymore
#![allow(dead_code, unused_imports)]
#[macro_use]
extern crate slog;
extern crate slog_term;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate serde;
extern crate futures;
extern crate hyper;
extern crate tokio_core;
extern crate hyper_tls;
extern crate time;

use std::env;
use std::io::{self, Write};

use serde::{Serialize, Serializer, Deserialize, Deserializer};
use futures::{Future, Stream};
use futures::future;
use slog::*;
use hyper::{Client, Uri, Method, Chunk, Error, StatusCode};
use hyper::header::{Authorization, Accept, UserAgent, qitem};
use hyper::client::{Request, Response, FutureResponse};
use hyper_tls::HttpsConnector;
use tokio_core::reactor::{Core, Handle};
mod utils;

///
/// 'Main' struct which have relevant parts which are use as core elements of the application
///
struct Main {
    core: Core,
    endpoint: HnNews,
    client: Client<hyper_tls::HttpsConnector>,
    logger: Logger,
}

fn create_main() -> Main {
    let logger = create_loggers();
    let mut core = Core::new().unwrap();
    let handle = core.handle();
    let client = configure_client(&handle);
    let endpoint = HnNews::build_default();
    let mut main = Main {
        core: core,
        endpoint: endpoint,
        client: client,
        logger: logger,
    };
    main
}
fn configure_client(handle: &Handle) -> Client<hyper_tls::HttpsConnector> {
    Client::configure()
        // Does not check the validity of certificate
        .connector(HttpsConnector::new(4, &handle))
        .build(&handle)
}

fn main() {
    let mut main = create_main();
    info!(&main.logger, "Application started");
    let response = get_top_story_ids(&mut main);
    println!("{}", response.unwrap());
}

fn get_comments_for_a_story(main: &mut Main, story_id: i32) {}

fn get_top_story_ids(main: &mut Main) -> Result<String, hyper::Error> {
    let logger = &main.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &main.endpoint;
    let client = &main.client;
    let work = request_top_story_ids(&client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger, &endpoint.get_top_stories_path(), &res.status());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| String::from_utf8(chunks).unwrap());
    let result = main.core.run(work);
    result
}

fn get_item_by_id(item: &str, main: &mut Main) -> Result<HnItem, hyper::Error> {
    let logger = &main.logger; // These need to be here as otherwise it'll cause mutable<>immutable borrow error
    let endpoint = &main.endpoint;
    let client = &main.client;
    let work = request_item(&item, &client, &endpoint)
        .and_then(|res| {
            log_response_status(&logger, &endpoint.get_item_path(&item), &res.status());
            res.body()
                .fold(Vec::new(), |mut v, chunk| {
                    v.extend(&chunk[..]);
                    future::ok::<_, Error>(v)
                })
        })
        .map(|chunks| {
            let s = String::from_utf8(chunks).unwrap();
            let deserialized: HnItem = serde_json::from_str(&s).unwrap();
            deserialized
        });
    let result = main.core.run(work);
    result
}

fn log_response_status(logger: &Logger, url: &str, status: &StatusCode) {
    info!(logger,
          format!("Request to {} finished with status {}", url, status));
}

fn common_headers(req: &mut Request) {
    req.headers_mut().set(UserAgent::new("hyper"));
}
fn consume_request(client: &Client<hyper_tls::HttpsConnector>, request: Request) -> FutureResponse {
    client.request(request)
}

fn create_loggers() -> Logger {
    let drain = slog_term::streamer().build().fuse();
    let root_logger = Logger::root(drain, o!());
    root_logger
}

fn request_top_story_ids(client: &Client<hyper_tls::HttpsConnector>,
                         endpoints: &HnNews)
                         -> FutureResponse {
    let url = utils::parse_url_from_str(&endpoints.get_top_stories_path());
    let mut request = Request::new(Method::Get, url);
    common_headers(&mut request);
    client.request(request)
}
fn request_item(item: &str,
                client: &Client<hyper_tls::HttpsConnector>,
                endpoints: &HnNews)
                -> FutureResponse {
    let url = utils::parse_url_from_str(&endpoints.get_item_path(item));
    let mut request = Request::new(Method::Get, url);
    common_headers(&mut request);
    client.request(request)
}

struct HnNews {
    base_url: String,
    top_news_suffix: String,
    item_suffix: String,
    max_item_suffix: String,
    json_suffix: String,
}

impl HnNews {
    pub fn build_default() -> HnNews {
        let e = HnNews {
            base_url: String::from("https://hacker-news.firebaseio.com/v0/"),
            top_news_suffix: String::from("topstories"),
            item_suffix: String::from("item/"),
            max_item_suffix: String::from("maxitem"),
            json_suffix: String::from(".json"),
        };
        e
    }

    pub fn get_top_stories_path(&self) -> String {
        utils::combine_strings(vec![&self.base_url, &self.top_news_suffix, &self.json_suffix])
    }
    pub fn get_max_item_path(&self) -> String {
        utils::combine_strings(vec![&self.base_url, &self.max_item_suffix, &self.json_suffix])
    }
    pub fn get_item_path(&self, id: &str) -> String {
        utils::combine_strings(vec![&self.base_url, &self.item_suffix, id, &self.json_suffix])
    }
}

#[derive(Serialize)]
struct HnTopStories {
    values: Vec<i32>,
}

impl Deserialize for HnTopStories {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: Deserializer
    {
        Deserialize::deserialize(deserializer).map(|arr: Vec<i32>| HnTopStories { values: arr })
    }
}

#[derive(Serialize, Deserialize)]
struct HnItem {
    by: String,
    descendants: i32,
    id: i32,
    kids: Vec<i32>,
    title: String,
    score: i32,
    time: f64,
    #[serde(rename(deserialize = "type"))]
    type_str: String,
    url: String,
}

#[derive(Serialize, Deserialize)]
struct HnUser {
    about: String,
    created: f64,
    id: String,
    karma: i32,
    submitted: Vec<i32>,
}

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

    #[test]
    fn hn_item_serde_test() {
        use std::fs::File;
        use std::io::prelude::*;
        let mut contents = String::new();
        File::open("res/test/item.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnItem = serde_json::from_str(&contents).unwrap();
        assert_eq!(71, deserialized.descendants);
        assert_eq!("dhouston", deserialized.by);
        assert_eq!(8863, deserialized.id);
        assert_eq!(111, deserialized.score);
        assert_eq!(1175714200.0f64, deserialized.time);
        assert_eq!("My YC app: Dropbox - Throw away your USB drive",
                   deserialized.title);
        assert_eq!("story", deserialized.type_str);
        assert_eq!("http://www.getdropbox.com/u/2/screencast.html",
                   deserialized.url);
    }
    #[test]
    fn hn_top_stories_serde_test() {
        use std::fs::File;
        use std::io::prelude::*;
        let mut contents = String::new();
        File::open("res/test/top-stories.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnTopStories = serde_json::from_str(&contents).unwrap();
        assert!(deserialized.values.len() > 3);
    }
    #[test]
    fn hn_user_serde_test() {
        use std::fs::File;
        use std::io::prelude::*;
        let mut contents = String::new();
        File::open("res/test/user.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnUser = serde_json::from_str(&contents).unwrap();
        assert_eq!("This is a test", deserialized.about);
        assert_eq!(1173923446.0f64, deserialized.created);
        assert_eq!("jl", deserialized.id);
        assert_eq!(3496, deserialized.karma);
        assert!(deserialized.submitted.len() > 3);
    }

    #[test]
    fn request_item_test() {
        let mut main = create_main();
        let s = String::from("8000");
        let response = main.core.run(request_item(&s, &main.client, &main.endpoint)).unwrap();
        assert_eq!(StatusCode::Ok, response.status());
    }

}
<|endoftext|>


<commit_before>/*!
The pathutil module provides platform specific operations on paths that are
typically faster than the same operations as provided in std::path. In
particular, we really want to avoid the costly operation of parsing the path
into its constituent components. We give up on Windows, but on Unix, we deal
with the raw bytes directly.

On large repositories (like chromium), this can have a ~25% performance
improvement on just listing the files to search (!).
*/
use std::ffi::OsStr;
use std::path::Path;

/// Strip `prefix` from the `path` and return the remainder.
///
/// If `path` doesn't have a prefix `prefix`, then return `None`.
#[cfg(unix)]
pub fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
    prefix: &'a P,
    path: &'a Path,
) -> Option<&'a Path> {
    use std::os::unix::ffi::OsStrExt;

    let prefix = prefix.as_ref().as_os_str().as_bytes();
    let path = path.as_os_str().as_bytes();
    if prefix.len() > path.len() || prefix != &path[0..prefix.len()] {
        None
    } else {
        Some(&Path::new(OsStr::from_bytes(&path[prefix.len()..])))
    }
}

/// Strip `prefix` from the `path` and return the remainder.
///
/// If `path` doesn't have a prefix `prefix`, then return `None`.
#[cfg(not(unix))]
pub fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
    prefix: &'a P,
    path: &'a Path,
) -> Option<&'a Path> {
    path.strip_prefix(prefix).ok()
}

/// The final component of the path, if it is a normal file.
///
/// If the path terminates in ., .., or consists solely of a root of prefix,
/// file_name will return None.
#[cfg(unix)]
pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
    path: &'a P,
) -> Option<&'a OsStr> {
    use std::os::unix::ffi::OsStrExt;
    use memchr::memrchr;

    let path = path.as_ref().as_os_str().as_bytes();
    if path.is_empty() {
        return None;
    } else if path.len() == 1 && path[0] == b'.' {
        return None;
    } else if path.last() == Some(&b'.') {
        return None;
    } else if path.len() >= 2 && &path[path.len() - 2..] == &b".."[..] {
        return None;
    }
    let last_slash = memrchr(b'/', path).map(|i| i + 1).unwrap_or(0);
    Some(OsStr::from_bytes(&path[last_slash..]))
}

/// The final component of the path, if it is a normal file.
///
/// If the path terminates in ., .., or consists solely of a root of prefix,
/// file_name will return None.
#[cfg(not(unix))]
pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
    path: &'a P,
) -> Option<&'a OsStr> {
    path.as_ref().file_name()
}

/// Returns true if and only if this file path is considered to be hidden.
#[cfg(unix)]
pub fn is_hidden<P: AsRef<Path>>(path: P) -> bool {
    use std::os::unix::ffi::OsStrExt;

    if let Some(name) = file_name(path.as_ref()) {
        name.as_bytes().get(0) == Some(&b'.')
    } else {
        false
    }
}

/// Returns true if and only if this file path is considered to be hidden.
#[cfg(not(unix))]
pub fn is_hidden<P: AsRef<Path>>(path: P) -> bool {
    if let Some(name) = file_name(path.as_ref()) {
        name.to_str().map(|s| s.starts_with(".")).unwrap_or(false)
    } else {
        false
    }
}

/// Returns true if this file path is just a file name. i.e., Its parent is
/// the empty string.
#[cfg(unix)]
pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
    use std::os::unix::ffi::OsStrExt;
    use memchr::memchr;

    let path = path.as_ref().as_os_str().as_bytes();
    memchr(b'/', path).is_none()
}

/// Returns true if this file path is just a file name. i.e., Its parent is
/// the empty string.
#[cfg(not(unix))]
pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
    path.as_ref().parent().map(|p| p.is_empty()).unwrap_or(false)
}
<commit_msg>Fix Windows compilation error.<commit_after>/*!
The pathutil module provides platform specific operations on paths that are
typically faster than the same operations as provided in std::path. In
particular, we really want to avoid the costly operation of parsing the path
into its constituent components. We give up on Windows, but on Unix, we deal
with the raw bytes directly.

On large repositories (like chromium), this can have a ~25% performance
improvement on just listing the files to search (!).
*/
use std::ffi::OsStr;
use std::path::Path;

/// Strip `prefix` from the `path` and return the remainder.
///
/// If `path` doesn't have a prefix `prefix`, then return `None`.
#[cfg(unix)]
pub fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
    prefix: &'a P,
    path: &'a Path,
) -> Option<&'a Path> {
    use std::os::unix::ffi::OsStrExt;

    let prefix = prefix.as_ref().as_os_str().as_bytes();
    let path = path.as_os_str().as_bytes();
    if prefix.len() > path.len() || prefix != &path[0..prefix.len()] {
        None
    } else {
        Some(&Path::new(OsStr::from_bytes(&path[prefix.len()..])))
    }
}

/// Strip `prefix` from the `path` and return the remainder.
///
/// If `path` doesn't have a prefix `prefix`, then return `None`.
#[cfg(not(unix))]
pub fn strip_prefix<'a, P: AsRef<Path> + ?Sized>(
    prefix: &'a P,
    path: &'a Path,
) -> Option<&'a Path> {
    path.strip_prefix(prefix).ok()
}

/// The final component of the path, if it is a normal file.
///
/// If the path terminates in ., .., or consists solely of a root of prefix,
/// file_name will return None.
#[cfg(unix)]
pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
    path: &'a P,
) -> Option<&'a OsStr> {
    use std::os::unix::ffi::OsStrExt;
    use memchr::memrchr;

    let path = path.as_ref().as_os_str().as_bytes();
    if path.is_empty() {
        return None;
    } else if path.len() == 1 && path[0] == b'.' {
        return None;
    } else if path.last() == Some(&b'.') {
        return None;
    } else if path.len() >= 2 && &path[path.len() - 2..] == &b".."[..] {
        return None;
    }
    let last_slash = memrchr(b'/', path).map(|i| i + 1).unwrap_or(0);
    Some(OsStr::from_bytes(&path[last_slash..]))
}

/// The final component of the path, if it is a normal file.
///
/// If the path terminates in ., .., or consists solely of a root of prefix,
/// file_name will return None.
#[cfg(not(unix))]
pub fn file_name<'a, P: AsRef<Path> + ?Sized>(
    path: &'a P,
) -> Option<&'a OsStr> {
    path.as_ref().file_name()
}

/// Returns true if and only if this file path is considered to be hidden.
#[cfg(unix)]
pub fn is_hidden<P: AsRef<Path>>(path: P) -> bool {
    use std::os::unix::ffi::OsStrExt;

    if let Some(name) = file_name(path.as_ref()) {
        name.as_bytes().get(0) == Some(&b'.')
    } else {
        false
    }
}

/// Returns true if and only if this file path is considered to be hidden.
#[cfg(not(unix))]
pub fn is_hidden<P: AsRef<Path>>(path: P) -> bool {
    if let Some(name) = file_name(path.as_ref()) {
        name.to_str().map(|s| s.starts_with(".")).unwrap_or(false)
    } else {
        false
    }
}

/// Returns true if this file path is just a file name. i.e., Its parent is
/// the empty string.
#[cfg(unix)]
pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
    use std::os::unix::ffi::OsStrExt;
    use memchr::memchr;

    let path = path.as_ref().as_os_str().as_bytes();
    memchr(b'/', path).is_none()
}

/// Returns true if this file path is just a file name. i.e., Its parent is
/// the empty string.
#[cfg(not(unix))]
pub fn is_file_name<P: AsRef<Path>>(path: P) -> bool {
    path.as_ref().parent().map(|p| p.as_os_str().is_empty()).unwrap_or(false)
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use libc::{c_double, c_int, c_uint};
use std::ptr;
use std::fmt;
use std::ops::Deref;
use ::enums::{
    Extend,
    Filter,
    PatternType,
    Status,
};
use ::enums::MeshCorner;
use ffi;
use ffi::{
    cairo_pattern_t,
    cairo_surface_t,
};
use ::{
    Path,
    Matrix,
    MatrixTrait,
    Surface,
};

// See http://cairographics.org/manual/bindings-patterns.html for more info
#[derive(Debug)]
pub struct Pattern {
    pointer: *mut cairo_pattern_t,
}

impl fmt::Display for Pattern {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Pattern")
    }
}

impl Pattern {
    user_data_methods! {
        ffi::cairo_pattern_get_user_data,
        ffi::cairo_pattern_set_user_data,
    }

    pub fn to_raw_none(&self) -> *mut cairo_pattern_t {
        self.pointer
    }

    pub unsafe fn from_raw_none(pointer: *mut cairo_pattern_t) -> Pattern {
        ffi::cairo_pattern_reference(pointer);
        Self::from_raw_full(pointer)
    }

    pub unsafe fn from_raw_full(pointer: *mut cairo_pattern_t) -> Pattern {
        Pattern { pointer }
    }

    pub fn get_type(&self) -> PatternType {
        unsafe {
            ffi::cairo_pattern_get_type(self.pointer).into()
        }
    }

    pub fn ensure_status(&self) {
        self.status().ensure_valid();
    }

    pub fn status(&self) -> Status {
        unsafe {
            Status::from(ffi::cairo_pattern_status(self.pointer))
        }
    }

    pub fn get_reference_count(&self) -> isize {
        unsafe {
            ffi::cairo_pattern_get_reference_count(self.pointer) as isize
        }
    }

    pub fn set_extend(&self, extend: Extend) {
        unsafe {
            ffi::cairo_pattern_set_extend(self.pointer, extend.into())
        }
    }

    pub fn get_extend(&self) -> Extend {
        unsafe {
            Extend::from(ffi::cairo_pattern_get_extend(self.pointer))
        }
    }

    pub fn set_filter(&self, filter: Filter) {
        unsafe {
            ffi::cairo_pattern_set_filter(self.pointer, filter.into())
        }
    }

    pub fn get_filter(&self) -> Filter {
        unsafe {
            Filter::from(ffi::cairo_pattern_get_filter(self.pointer))
        }
    }

    pub fn set_matrix(&self, matrix: Matrix) {
        unsafe {
            ffi::cairo_pattern_set_matrix (self.pointer, &matrix)
        }
    }

    pub fn get_matrix(&self) -> Matrix {
        let mut matrix = Matrix::null();
        unsafe {
            ffi::cairo_pattern_get_matrix(self.pointer, &mut matrix);
        }
        matrix
    }
}

impl Clone for Pattern {
    fn clone(&self) -> Self {
        Pattern {
            pointer: unsafe {
                ffi::cairo_pattern_reference(self.pointer)
            },
        }
    }
}

impl Drop for Pattern {
    fn drop(&mut self){
        unsafe {
            ffi::cairo_pattern_destroy(self.pointer)
        }
    }
}

macro_rules! pattern_type(
    //Signals without arguments
    ($pattern_type:ident) => (

        #[derive(Debug, Clone)]
        pub struct $pattern_type(Pattern);

        impl Deref for $pattern_type {
            type Target = Pattern;

            fn deref(&self) -> &Pattern {
                &self.0
            }
        }

        impl fmt::Display for $pattern_type {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, stringify!($pattern_type))
            }
        }
    );
);

pattern_type!(SolidPattern);

impl SolidPattern {
    pub fn from_rgb(red: f64, green: f64, blue: f64) -> SolidPattern {
        unsafe {
            SolidPattern(Pattern::from_raw_full(
                ffi::cairo_pattern_create_rgb(red, green, blue)
            ))
        }
    }

    pub fn from_rgba(red: f64, green: f64, blue: f64, alpha: f64) -> SolidPattern {
        unsafe {
            SolidPattern(Pattern::from_raw_full(
                ffi::cairo_pattern_create_rgba(red, green, blue, alpha)
            ))
        }
    }

    pub fn get_rgba(&self) -> (f64, f64, f64, f64) {
        unsafe {
            let mut red   = 0.0;
            let mut green = 0.0;
            let mut blue  = 0.0;
            let mut alpha = 0.0;

            Status::from(ffi::cairo_pattern_get_rgba(self.pointer,
                                                     &mut red,
                                                     &mut green,
                                                     &mut blue,
                                                     &mut alpha)).ensure_valid();

            (red, green, blue, alpha)
        }
    }
}

macro_rules! gradient_type {
    ($gradient_type: ident) => {
        pattern_type!($gradient_type);

        impl $gradient_type {
            pub fn add_color_stop_rgb(&self, offset: f64, red: f64, green: f64, blue: f64) {
                unsafe {
                    ffi::cairo_pattern_add_color_stop_rgb(self.pointer, offset, red, green, blue)
                }
            }

            pub fn add_color_stop_rgba(&self, offset: f64, red: f64, green: f64, blue: f64, alpha: f64) {
                unsafe {
                    ffi::cairo_pattern_add_color_stop_rgba(self.pointer, offset, red, green, blue, alpha)
                }
            }

            pub fn get_color_stop_count(&self) -> isize {
                unsafe {
                    let mut count = 0;
                    let result = ffi::cairo_pattern_get_color_stop_count(self.pointer, &mut count);

                    Status::from(result).ensure_valid(); // Not sure if these are needed
                    count as isize
                }
            }

            pub fn get_color_stop_rgba(&self, index: isize) -> (f64, f64, f64, f64, f64) {
                unsafe {
                    let mut offset = 0.0;
                    let mut red    = 0.0;
                    let mut green  = 0.0;
                    let mut blue   = 0.0;
                    let mut alpha  = 0.0;

                    Status::from(ffi::cairo_pattern_get_color_stop_rgba(self.pointer,
                                                                        index as c_int,
                                                                        &mut offset,
                                                                        &mut red,
                                                                        &mut green,
                                                                        &mut blue,
                                                                        &mut alpha)).ensure_valid();
                    (offset, red, green, blue, alpha)
                }
            }
        }
    }
}

gradient_type!(LinearGradient);

impl LinearGradient {
    pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> LinearGradient {
        unsafe {
            LinearGradient(Pattern::from_raw_full(
                ffi::cairo_pattern_create_linear(x0, y0, x1, y1)
            ))
        }
    }

    pub fn get_linear_points(&self) -> (f64, f64, f64, f64) {
        unsafe {
            let mut x0 = 0.0;
            let mut y0 = 0.0;
            let mut x1 = 0.0;
            let mut y1 = 0.0;

            Status::from(ffi::cairo_pattern_get_linear_points(self.pointer,
                                                              &mut x0,
                                                              &mut y0,
                                                              &mut x1,
                                                              &mut y1)).ensure_valid();
            (x0, y0, x1, y1)
        }
    }
}

gradient_type!(RadialGradient);

impl RadialGradient {
    pub fn new(x0: f64, y0: f64, r0: f64, x1: f64, y1: f64, r1: f64) -> RadialGradient {
        unsafe {
            RadialGradient(Pattern::from_raw_full(
                ffi::cairo_pattern_create_radial(x0, y0, r0, x1, y1, r1)
            ))
        }
    }

    pub fn get_radial_circles(&self) -> (f64, f64, f64, f64, f64, f64) {
        unsafe {
            let mut x0 = 0.0;
            let mut y0 = 0.0;
            let mut r0 = 0.0;
            let mut x1 = 0.0;
            let mut y1 = 0.0;
            let mut r1 = 0.0;

            Status::from(ffi::cairo_pattern_get_radial_circles(self.pointer,
                                                               &mut x0,
                                                               &mut y0,
                                                               &mut r0,
                                                               &mut x1,
                                                               &mut y1,
                                                               &mut r1)).ensure_valid();
            (x0, y0, r0, x1, y1, r1)
        }
    }
}

pattern_type!(SurfacePattern);

impl SurfacePattern {
    pub fn create<T: AsRef<Surface>>(surface: &T) -> SurfacePattern {
        unsafe {
            SurfacePattern(Pattern::from_raw_full(
                ffi::cairo_pattern_create_for_surface(surface.as_ref().to_raw_none())
            ))
        }
    }

    pub fn get_surface(&self) -> Surface {
        unsafe {
            let mut surface_ptr: *mut cairo_surface_t = ptr::null_mut();
            Status::from(ffi::cairo_pattern_get_surface(self.pointer,
                                                        &mut surface_ptr)).ensure_valid();
            Surface::from_raw_none(surface_ptr)
        }
    }
}

pattern_type!(Mesh);

impl Mesh {
    pub fn new() -> Mesh {
        unsafe {
            Mesh(Pattern::from_raw_full(
                ffi::cairo_pattern_create_mesh()
            ))
        }
    }

    pub fn begin_patch(&self) {
        unsafe {
            ffi::cairo_mesh_pattern_begin_patch(self.pointer)
        }
        self.ensure_status();
    }

    pub fn end_patch(&self) {
        unsafe {
            ffi::cairo_mesh_pattern_end_patch(self.pointer)
        }
        self.ensure_status();
    }

    pub fn move_to(&self, x: f64, y: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_move_to(self.pointer, x, y)
        }
        self.ensure_status();
    }

    pub fn line_to(&self, x: f64, y: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_line_to(self.pointer, x, y)
        }
        self.ensure_status();
    }

    pub fn curve_to(&self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_curve_to(self.pointer, x1, y1, x2, y2, x3, y3)
        }
        self.ensure_status();
    }

    pub fn set_control_point(&self, corner: MeshCorner, x: f64, y: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_set_control_point(self.pointer, corner.into(), x, y)
        }
        self.ensure_status();
    }

    pub fn get_control_point(&self, patch_num: usize, corner: MeshCorner) -> (f64, f64) {
        let mut x: c_double = 0.0;
        let mut y: c_double = 0.0;

        let status = unsafe {
            ffi::cairo_mesh_pattern_get_control_point(self.pointer,
                                                      patch_num as c_uint,
                                                      corner.into(),
                                                      &mut x,
                                                      &mut y)
        };
        Status::from(status).ensure_valid();
        (x, y)
    }

    pub fn set_corner_color_rgb(&self, corner: MeshCorner, red: f64, green: f64, blue: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_set_corner_color_rgb(self.pointer,
                                                         corner.into(),
                                                         red,
                                                         green,
                                                         blue)
        }
        self.ensure_status();
    }

    pub fn set_corner_color_rgba(&self, corner: MeshCorner, red: f64, green: f64, blue: f64, alpha: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_set_corner_color_rgba(self.pointer,
                                                          corner.into(),
                                                          red,
                                                          green,
                                                          blue,
                                                          alpha)
        }
        self.ensure_status();
    }

    pub fn get_corner_color_rgba(&self, patch_num: usize, corner: MeshCorner) -> (f64, f64, f64, f64) {
        let mut red: c_double = 0.0;
        let mut green: c_double = 0.0;
        let mut blue: c_double = 0.0;
        let mut alpha: c_double = 0.0;

        let status = unsafe {
            ffi::cairo_mesh_pattern_get_corner_color_rgba(self.pointer,
                                                          patch_num as c_uint,
                                                          corner.into(),
                                                          &mut red,
                                                          &mut green,
                                                          &mut blue,
                                                          &mut alpha)
        };
        Status::from(status).ensure_valid();
        (red, green, blue, alpha)
    }

    pub fn get_patch_count(&self) -> usize {
        let mut count: c_uint = 0;
        unsafe {
            Status::from(ffi::cairo_mesh_pattern_get_patch_count(self.pointer,
                                                                 &mut count)).ensure_valid();
        }
        count as usize
    }

    pub fn get_path(&self, patch_num: usize) -> Path {
        let path: Path = unsafe {
            Path::from_raw_full(
                ffi::cairo_mesh_pattern_get_path(self.pointer, patch_num as c_uint)
            )
        };
        path.ensure_status();
        path
    }
}
<commit_msg>Introduce an intermediate Gradient type<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use libc::{c_double, c_int, c_uint};
use std::ptr;
use std::fmt;
use std::ops::Deref;
use ::enums::{
    Extend,
    Filter,
    PatternType,
    Status,
};
use ::enums::MeshCorner;
use ffi;
use ffi::{
    cairo_pattern_t,
    cairo_surface_t,
};
use ::{
    Path,
    Matrix,
    MatrixTrait,
    Surface,
};

// See http://cairographics.org/manual/bindings-patterns.html for more info
#[derive(Debug)]
pub struct Pattern {
    pointer: *mut cairo_pattern_t,
}

impl fmt::Display for Pattern {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Pattern")
    }
}

impl Pattern {
    user_data_methods! {
        ffi::cairo_pattern_get_user_data,
        ffi::cairo_pattern_set_user_data,
    }

    pub fn to_raw_none(&self) -> *mut cairo_pattern_t {
        self.pointer
    }

    pub unsafe fn from_raw_none(pointer: *mut cairo_pattern_t) -> Pattern {
        ffi::cairo_pattern_reference(pointer);
        Self::from_raw_full(pointer)
    }

    pub unsafe fn from_raw_full(pointer: *mut cairo_pattern_t) -> Pattern {
        Pattern { pointer }
    }

    pub fn get_type(&self) -> PatternType {
        unsafe {
            ffi::cairo_pattern_get_type(self.pointer).into()
        }
    }

    pub fn ensure_status(&self) {
        self.status().ensure_valid();
    }

    pub fn status(&self) -> Status {
        unsafe {
            Status::from(ffi::cairo_pattern_status(self.pointer))
        }
    }

    pub fn get_reference_count(&self) -> isize {
        unsafe {
            ffi::cairo_pattern_get_reference_count(self.pointer) as isize
        }
    }

    pub fn set_extend(&self, extend: Extend) {
        unsafe {
            ffi::cairo_pattern_set_extend(self.pointer, extend.into())
        }
    }

    pub fn get_extend(&self) -> Extend {
        unsafe {
            Extend::from(ffi::cairo_pattern_get_extend(self.pointer))
        }
    }

    pub fn set_filter(&self, filter: Filter) {
        unsafe {
            ffi::cairo_pattern_set_filter(self.pointer, filter.into())
        }
    }

    pub fn get_filter(&self) -> Filter {
        unsafe {
            Filter::from(ffi::cairo_pattern_get_filter(self.pointer))
        }
    }

    pub fn set_matrix(&self, matrix: Matrix) {
        unsafe {
            ffi::cairo_pattern_set_matrix (self.pointer, &matrix)
        }
    }

    pub fn get_matrix(&self) -> Matrix {
        let mut matrix = Matrix::null();
        unsafe {
            ffi::cairo_pattern_get_matrix(self.pointer, &mut matrix);
        }
        matrix
    }
}

impl Clone for Pattern {
    fn clone(&self) -> Self {
        Pattern {
            pointer: unsafe {
                ffi::cairo_pattern_reference(self.pointer)
            },
        }
    }
}

impl Drop for Pattern {
    fn drop(&mut self){
        unsafe {
            ffi::cairo_pattern_destroy(self.pointer)
        }
    }
}

macro_rules! pattern_type(
    //Signals without arguments
    ($pattern_type:ident) => (

        #[derive(Debug, Clone)]
        pub struct $pattern_type(Pattern);

        impl Deref for $pattern_type {
            type Target = Pattern;

            fn deref(&self) -> &Pattern {
                &self.0
            }
        }

        impl fmt::Display for $pattern_type {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, stringify!($pattern_type))
            }
        }
    );
);

pattern_type!(SolidPattern);

impl SolidPattern {
    pub fn from_rgb(red: f64, green: f64, blue: f64) -> SolidPattern {
        unsafe {
            SolidPattern(Pattern::from_raw_full(
                ffi::cairo_pattern_create_rgb(red, green, blue)
            ))
        }
    }

    pub fn from_rgba(red: f64, green: f64, blue: f64, alpha: f64) -> SolidPattern {
        unsafe {
            SolidPattern(Pattern::from_raw_full(
                ffi::cairo_pattern_create_rgba(red, green, blue, alpha)
            ))
        }
    }

    pub fn get_rgba(&self) -> (f64, f64, f64, f64) {
        unsafe {
            let mut red   = 0.0;
            let mut green = 0.0;
            let mut blue  = 0.0;
            let mut alpha = 0.0;

            Status::from(ffi::cairo_pattern_get_rgba(self.pointer,
                                                     &mut red,
                                                     &mut green,
                                                     &mut blue,
                                                     &mut alpha)).ensure_valid();

            (red, green, blue, alpha)
        }
    }
}

pattern_type!(Gradient);

impl Gradient {
    pub fn add_color_stop_rgb(&self, offset: f64, red: f64, green: f64, blue: f64) {
        unsafe {
            ffi::cairo_pattern_add_color_stop_rgb(self.pointer, offset, red, green, blue)
        }
    }

    pub fn add_color_stop_rgba(&self, offset: f64, red: f64, green: f64, blue: f64, alpha: f64) {
        unsafe {
            ffi::cairo_pattern_add_color_stop_rgba(self.pointer, offset, red, green, blue, alpha)
        }
    }

    pub fn get_color_stop_count(&self) -> isize {
        unsafe {
            let mut count = 0;
            let result = ffi::cairo_pattern_get_color_stop_count(self.pointer, &mut count);

            Status::from(result).ensure_valid(); // Not sure if these are needed
            count as isize
        }
    }

    pub fn get_color_stop_rgba(&self, index: isize) -> (f64, f64, f64, f64, f64) {
        unsafe {
            let mut offset = 0.0;
            let mut red    = 0.0;
            let mut green  = 0.0;
            let mut blue   = 0.0;
            let mut alpha  = 0.0;

            Status::from(ffi::cairo_pattern_get_color_stop_rgba(self.pointer,
                                                                index as c_int,
                                                                &mut offset,
                                                                &mut red,
                                                                &mut green,
                                                                &mut blue,
                                                                &mut alpha)).ensure_valid();
            (offset, red, green, blue, alpha)
        }
    }
}

macro_rules! gradient_type {
    ($gradient_type: ident) => {
        #[derive(Debug, Clone)]
        pub struct $gradient_type(Gradient);

        impl Deref for $gradient_type {
            type Target = Gradient;

            fn deref(&self) -> &Gradient {
                &self.0
            }
        }

        impl fmt::Display for $gradient_type {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, stringify!($gradient_type))
            }
        }
    }
}

gradient_type!(LinearGradient);

impl LinearGradient {
    pub fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> LinearGradient {
        unsafe {
            LinearGradient(Gradient(Pattern::from_raw_full(
                ffi::cairo_pattern_create_linear(x0, y0, x1, y1)
            )))
        }
    }

    pub fn get_linear_points(&self) -> (f64, f64, f64, f64) {
        unsafe {
            let mut x0 = 0.0;
            let mut y0 = 0.0;
            let mut x1 = 0.0;
            let mut y1 = 0.0;

            Status::from(ffi::cairo_pattern_get_linear_points(self.pointer,
                                                              &mut x0,
                                                              &mut y0,
                                                              &mut x1,
                                                              &mut y1)).ensure_valid();
            (x0, y0, x1, y1)
        }
    }
}

gradient_type!(RadialGradient);

impl RadialGradient {
    pub fn new(x0: f64, y0: f64, r0: f64, x1: f64, y1: f64, r1: f64) -> RadialGradient {
        unsafe {
            RadialGradient(Gradient(Pattern::from_raw_full(
                ffi::cairo_pattern_create_radial(x0, y0, r0, x1, y1, r1)
            )))
        }
    }

    pub fn get_radial_circles(&self) -> (f64, f64, f64, f64, f64, f64) {
        unsafe {
            let mut x0 = 0.0;
            let mut y0 = 0.0;
            let mut r0 = 0.0;
            let mut x1 = 0.0;
            let mut y1 = 0.0;
            let mut r1 = 0.0;

            Status::from(ffi::cairo_pattern_get_radial_circles(self.pointer,
                                                               &mut x0,
                                                               &mut y0,
                                                               &mut r0,
                                                               &mut x1,
                                                               &mut y1,
                                                               &mut r1)).ensure_valid();
            (x0, y0, r0, x1, y1, r1)
        }
    }
}

pattern_type!(SurfacePattern);

impl SurfacePattern {
    pub fn create<T: AsRef<Surface>>(surface: &T) -> SurfacePattern {
        unsafe {
            SurfacePattern(Pattern::from_raw_full(
                ffi::cairo_pattern_create_for_surface(surface.as_ref().to_raw_none())
            ))
        }
    }

    pub fn get_surface(&self) -> Surface {
        unsafe {
            let mut surface_ptr: *mut cairo_surface_t = ptr::null_mut();
            Status::from(ffi::cairo_pattern_get_surface(self.pointer,
                                                        &mut surface_ptr)).ensure_valid();
            Surface::from_raw_none(surface_ptr)
        }
    }
}

pattern_type!(Mesh);

impl Mesh {
    pub fn new() -> Mesh {
        unsafe {
            Mesh(Pattern::from_raw_full(
                ffi::cairo_pattern_create_mesh()
            ))
        }
    }

    pub fn begin_patch(&self) {
        unsafe {
            ffi::cairo_mesh_pattern_begin_patch(self.pointer)
        }
        self.ensure_status();
    }

    pub fn end_patch(&self) {
        unsafe {
            ffi::cairo_mesh_pattern_end_patch(self.pointer)
        }
        self.ensure_status();
    }

    pub fn move_to(&self, x: f64, y: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_move_to(self.pointer, x, y)
        }
        self.ensure_status();
    }

    pub fn line_to(&self, x: f64, y: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_line_to(self.pointer, x, y)
        }
        self.ensure_status();
    }

    pub fn curve_to(&self, x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_curve_to(self.pointer, x1, y1, x2, y2, x3, y3)
        }
        self.ensure_status();
    }

    pub fn set_control_point(&self, corner: MeshCorner, x: f64, y: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_set_control_point(self.pointer, corner.into(), x, y)
        }
        self.ensure_status();
    }

    pub fn get_control_point(&self, patch_num: usize, corner: MeshCorner) -> (f64, f64) {
        let mut x: c_double = 0.0;
        let mut y: c_double = 0.0;

        let status = unsafe {
            ffi::cairo_mesh_pattern_get_control_point(self.pointer,
                                                      patch_num as c_uint,
                                                      corner.into(),
                                                      &mut x,
                                                      &mut y)
        };
        Status::from(status).ensure_valid();
        (x, y)
    }

    pub fn set_corner_color_rgb(&self, corner: MeshCorner, red: f64, green: f64, blue: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_set_corner_color_rgb(self.pointer,
                                                         corner.into(),
                                                         red,
                                                         green,
                                                         blue)
        }
        self.ensure_status();
    }

    pub fn set_corner_color_rgba(&self, corner: MeshCorner, red: f64, green: f64, blue: f64, alpha: f64) {
        unsafe {
            ffi::cairo_mesh_pattern_set_corner_color_rgba(self.pointer,
                                                          corner.into(),
                                                          red,
                                                          green,
                                                          blue,
                                                          alpha)
        }
        self.ensure_status();
    }

    pub fn get_corner_color_rgba(&self, patch_num: usize, corner: MeshCorner) -> (f64, f64, f64, f64) {
        let mut red: c_double = 0.0;
        let mut green: c_double = 0.0;
        let mut blue: c_double = 0.0;
        let mut alpha: c_double = 0.0;

        let status = unsafe {
            ffi::cairo_mesh_pattern_get_corner_color_rgba(self.pointer,
                                                          patch_num as c_uint,
                                                          corner.into(),
                                                          &mut red,
                                                          &mut green,
                                                          &mut blue,
                                                          &mut alpha)
        };
        Status::from(status).ensure_valid();
        (red, green, blue, alpha)
    }

    pub fn get_patch_count(&self) -> usize {
        let mut count: c_uint = 0;
        unsafe {
            Status::from(ffi::cairo_mesh_pattern_get_patch_count(self.pointer,
                                                                 &mut count)).ensure_valid();
        }
        count as usize
    }

    pub fn get_path(&self, patch_num: usize) -> Path {
        let path: Path = unsafe {
            Path::from_raw_full(
                ffi::cairo_mesh_pattern_get_path(self.pointer, patch_num as c_uint)
            )
        };
        path.ensure_status();
        path
    }
}
<|endoftext|>


<commit_before>use crate::error;
use alloc::string::ToString;
use scroll::Pread;

use super::options;
use super::section_table;

use crate::pe::data_directories::DataDirectory;
use core::cmp;

use log::debug;

pub fn is_in_range(rva: usize, r1: usize, r2: usize) -> bool {
    r1 <= rva && rva < r2
}

// reference: Peter Ferrie. Reliable algorithm to extract overlay of a PE. https://bit.ly/2vBX2bR
#[inline]
fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize {
    const PHYSICAL_ALIGN: usize = 0x1ff;
    pointer_to_raw_data & !PHYSICAL_ALIGN
}

#[inline]
fn section_read_size(section: &section_table::SectionTable, file_alignment: u32) -> usize {
    fn round_size(size: usize) -> usize {
        const PAGE_MASK: usize = 0xfff;
        (size + PAGE_MASK) & !PAGE_MASK
    }

    // Paraphrased from https://reverseengineering.stackexchange.com/a/4326 (by Peter Ferrie).
    //
    // Handles the corner cases such as mis-aligned pointers (round down) and sizes (round up)
    // Further rounding corner cases:
    // - the physical pointer should be rounded down to a multiple of 512, regardless of the value in the header
    // - the read size is rounded up by using a combination of the file alignment and 4kb
    // - the virtual size is always rounded up to a multiple of 4kb, regardless of the value in the header.
    //
    // Reference C implementation:
    //
    // long pointerToRaw = section.get(POINTER_TO_RAW_DATA);
    // long alignedpointerToRaw = pointerToRaw & ~0x1ff;
    // long sizeOfRaw = section.get(SIZE_OF_RAW_DATA);
    // long readsize = ((pointerToRaw + sizeOfRaw) + filealign - 1) & ~(filealign - 1)) - alignedpointerToRaw;
    // readsize = min(readsize, (sizeOfRaw + 0xfff) & ~0xfff);
    // long virtsize = section.get(VIRTUAL_SIZE);
    //
    // if (virtsize)
    // {
    //     readsize = min(readsize, (virtsize + 0xfff) & ~0xfff);
    // }

    let file_alignment = file_alignment as usize;
    let size_of_raw_data = section.size_of_raw_data as usize;
    let virtual_size = section.virtual_size as usize;
    let read_size = {
        let read_size =
            ((section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment - 1)
                & !(file_alignment - 1))
                - aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize);
        cmp::min(read_size, round_size(size_of_raw_data))
    };

    if virtual_size == 0 {
        read_size
    } else {
        cmp::min(read_size, round_size(virtual_size))
    }
}

fn rva2offset(rva: usize, section: &section_table::SectionTable) -> usize {
    (rva - section.virtual_address as usize)
        + aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize)
}

fn is_in_section(rva: usize, section: &section_table::SectionTable, file_alignment: u32) -> bool {
    let section_rva = section.virtual_address as usize;
    is_in_range(
        rva,
        section_rva,
        section_rva + section_read_size(section, file_alignment),
    )
}

pub fn find_offset(
    rva: usize,
    sections: &[section_table::SectionTable],
    file_alignment: u32,
    opts: &options::ParseOptions,
) -> Option<usize> {
    if opts.resolve_rva {
        for (i, section) in sections.iter().enumerate() {
            debug!(
                "Checking {} for {:#x} ∈ {:#x}..{:#x}",
                section.name().unwrap_or(""),
                rva,
                section.virtual_address,
                section.virtual_address + section.virtual_size
            );
            if is_in_section(rva, &section, file_alignment) {
                let offset = rva2offset(rva, &section);
                debug!(
                    "Found in section {}({}), remapped into offset {:#x}",
                    section.name().unwrap_or(""),
                    i,
                    offset
                );
                return Some(offset);
            }
        }
        None
    } else {
        Some(rva)
    }
}

pub fn find_offset_or(
    rva: usize,
    sections: &[section_table::SectionTable],
    file_alignment: u32,
    opts: &options::ParseOptions,
    msg: &str,
) -> error::Result<usize> {
    find_offset(rva, sections, file_alignment, opts)
        .ok_or_else(|| error::Error::Malformed(msg.to_string()))
}

pub fn try_name<'a>(
    bytes: &'a [u8],
    rva: usize,
    sections: &[section_table::SectionTable],
    file_alignment: u32,
    opts: &options::ParseOptions,
) -> error::Result<&'a str> {
    match find_offset(rva, sections, file_alignment, opts) {
        Some(offset) => Ok(bytes.pread::<&str>(offset)?),
        None => Err(error::Error::Malformed(format!(
            "Cannot find name from rva {:#x} in sections: {:?}",
            rva, sections
        ))),
    }
}

pub fn get_data<'a, T>(
    bytes: &'a [u8],
    sections: &[section_table::SectionTable],
    directory: DataDirectory,
    file_alignment: u32,
) -> error::Result<T>
where
    T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
{
    get_data_with_opts(
        bytes,
        sections,
        directory,
        file_alignment,
        &options::ParseOptions::default(),
    )
}

pub fn get_data_with_opts<'a, T>(
    bytes: &'a [u8],
    sections: &[section_table::SectionTable],
    directory: DataDirectory,
    file_alignment: u32,
    opts: &options::ParseOptions,
) -> error::Result<T>
where
    T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
{
    let rva = directory.virtual_address as usize;
    let offset = find_offset(rva, sections, file_alignment, opts)
        .ok_or_else(|| error::Error::Malformed(directory.virtual_address.to_string()))?;
    let result: T = bytes.pread_with(offset, scroll::LE)?;
    Ok(result)
}
<commit_msg>pe.utils: file alignment check<commit_after>use crate::error;
use alloc::string::ToString;
use scroll::Pread;

use super::options;
use super::section_table;

use crate::pe::data_directories::DataDirectory;
use core::cmp;

use log::debug;

pub fn is_in_range(rva: usize, r1: usize, r2: usize) -> bool {
    r1 <= rva && rva < r2
}

// reference: Peter Ferrie. Reliable algorithm to extract overlay of a PE. https://bit.ly/2vBX2bR
#[inline]
fn aligned_pointer_to_raw_data(pointer_to_raw_data: usize) -> usize {
    const PHYSICAL_ALIGN: usize = 0x1ff;
    pointer_to_raw_data & !PHYSICAL_ALIGN
}

#[inline]
fn section_read_size(section: &section_table::SectionTable, file_alignment: u32) -> usize {
    fn round_size(size: usize) -> usize {
        const PAGE_MASK: usize = 0xfff;
        (size + PAGE_MASK) & !PAGE_MASK
    }

    // Paraphrased from https://reverseengineering.stackexchange.com/a/4326 (by Peter Ferrie).
    //
    // Handles the corner cases such as mis-aligned pointers (round down) and sizes (round up)
    // Further rounding corner cases:
    // - the physical pointer should be rounded down to a multiple of 512, regardless of the value in the header
    // - the read size is rounded up by using a combination of the file alignment and 4kb
    // - the virtual size is always rounded up to a multiple of 4kb, regardless of the value in the header.
    //
    // Reference C implementation:
    //
    // long pointerToRaw = section.get(POINTER_TO_RAW_DATA);
    // long alignedpointerToRaw = pointerToRaw & ~0x1ff;
    // long sizeOfRaw = section.get(SIZE_OF_RAW_DATA);
    // long readsize = ((pointerToRaw + sizeOfRaw) + filealign - 1) & ~(filealign - 1)) - alignedpointerToRaw;
    // readsize = min(readsize, (sizeOfRaw + 0xfff) & ~0xfff);
    // long virtsize = section.get(VIRTUAL_SIZE);
    //
    // if (virtsize)
    // {
    //     readsize = min(readsize, (virtsize + 0xfff) & ~0xfff);
    // }

    let file_alignment = file_alignment as usize;
    let size_of_raw_data = section.size_of_raw_data as usize;
    let virtual_size = section.virtual_size as usize;
    let read_size = {
        let read_size =
            ((section.pointer_to_raw_data as usize + size_of_raw_data + file_alignment - 1)
                & !(file_alignment - 1))
                - aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize);
        cmp::min(read_size, round_size(size_of_raw_data))
    };

    if virtual_size == 0 {
        read_size
    } else {
        cmp::min(read_size, round_size(virtual_size))
    }
}

fn rva2offset(rva: usize, section: &section_table::SectionTable) -> usize {
    (rva - section.virtual_address as usize)
        + aligned_pointer_to_raw_data(section.pointer_to_raw_data as usize)
}

fn is_in_section(rva: usize, section: &section_table::SectionTable, file_alignment: u32) -> bool {
    let section_rva = section.virtual_address as usize;
    is_in_range(
        rva,
        section_rva,
        section_rva + section_read_size(section, file_alignment),
    )
}

pub fn find_offset(
    rva: usize,
    sections: &[section_table::SectionTable],
    file_alignment: u32,
    opts: &options::ParseOptions,
) -> Option<usize> {
    if opts.resolve_rva {
        if file_alignment == 0 || file_alignment & (file_alignment - 1) != 0 {
            return None;
        }
        for (i, section) in sections.iter().enumerate() {
            debug!(
                "Checking {} for {:#x} ∈ {:#x}..{:#x}",
                section.name().unwrap_or(""),
                rva,
                section.virtual_address,
                section.virtual_address + section.virtual_size
            );
            if is_in_section(rva, &section, file_alignment) {
                let offset = rva2offset(rva, &section);
                debug!(
                    "Found in section {}({}), remapped into offset {:#x}",
                    section.name().unwrap_or(""),
                    i,
                    offset
                );
                return Some(offset);
            }
        }
        None
    } else {
        Some(rva)
    }
}

pub fn find_offset_or(
    rva: usize,
    sections: &[section_table::SectionTable],
    file_alignment: u32,
    opts: &options::ParseOptions,
    msg: &str,
) -> error::Result<usize> {
    find_offset(rva, sections, file_alignment, opts)
        .ok_or_else(|| error::Error::Malformed(msg.to_string()))
}

pub fn try_name<'a>(
    bytes: &'a [u8],
    rva: usize,
    sections: &[section_table::SectionTable],
    file_alignment: u32,
    opts: &options::ParseOptions,
) -> error::Result<&'a str> {
    match find_offset(rva, sections, file_alignment, opts) {
        Some(offset) => Ok(bytes.pread::<&str>(offset)?),
        None => Err(error::Error::Malformed(format!(
            "Cannot find name from rva {:#x} in sections: {:?}",
            rva, sections
        ))),
    }
}

pub fn get_data<'a, T>(
    bytes: &'a [u8],
    sections: &[section_table::SectionTable],
    directory: DataDirectory,
    file_alignment: u32,
) -> error::Result<T>
where
    T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
{
    get_data_with_opts(
        bytes,
        sections,
        directory,
        file_alignment,
        &options::ParseOptions::default(),
    )
}

pub fn get_data_with_opts<'a, T>(
    bytes: &'a [u8],
    sections: &[section_table::SectionTable],
    directory: DataDirectory,
    file_alignment: u32,
    opts: &options::ParseOptions,
) -> error::Result<T>
where
    T: scroll::ctx::TryFromCtx<'a, scroll::Endian, Error = scroll::Error>,
{
    let rva = directory.virtual_address as usize;
    let offset = find_offset(rva, sections, file_alignment, opts)
        .ok_or_else(|| error::Error::Malformed(directory.virtual_address.to_string()))?;
    let result: T = bytes.pread_with(offset, scroll::LE)?;
    Ok(result)
}
<|endoftext|>


<commit_before>use crate::{error::ParseError, Database};
use cfg_if::cfg_if;
use std::{convert::TryFrom, ops::Deref};
use url::Url;

cfg_if! {if #[cfg(feature = "with-postgres")]{
    use crate::pg::PostgresDB;
}}

cfg_if! {if #[cfg(feature = "with-sqlite")]{
    use crate::sqlite::SqliteDB;
}}

cfg_if! {if #[cfg(feature = "with-mysql")]{
    use crate::my::MysqlDB;
}}

pub enum DBPlatform {
    #[cfg(feature = "with-postgres")]
    Postgres(Box<PostgresDB>),
    #[cfg(feature = "with-sqlite")]
    Sqlite(Box<SqliteDB>),
    #[cfg(feature = "with-mysql")]
    Mysql(Box<MysqlDB>),
}

impl Deref for DBPlatform {
    type Target = dyn Database;

    fn deref(&self) -> &Self::Target {
        match *self {
            #[cfg(feature = "with-postgres")]
            DBPlatform::Postgres(ref pg) => pg.deref(),
            #[cfg(feature = "with-sqlite")]
            DBPlatform::Sqlite(ref sq) => sq.deref(),
            #[cfg(feature = "with-mysql")]
            DBPlatform::Mysql(ref my) => my.deref(),
        }
    }
}

impl std::ops::DerefMut for DBPlatform {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match *self {
            #[cfg(feature = "with-postgres")]
            DBPlatform::Postgres(ref mut pg) => pg.deref_mut(),
            #[cfg(feature = "with-sqlite")]
            DBPlatform::Sqlite(ref mut sq) => sq.deref_mut(),
            #[cfg(feature = "with-mysql")]
            DBPlatform::Mysql(ref mut my) => my.deref_mut(),
        }
    }
}

pub(crate) enum Platform {
    #[cfg(feature = "with-postgres")]
    Postgres,
    #[cfg(feature = "with-sqlite")]
    Sqlite(String),
    #[cfg(feature = "with-mysql")]
    Mysql,
    Unsupported(String),
}

impl<'a> TryFrom<&'a str> for Platform {
    type Error = ParseError;

    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
        let url = Url::parse(s);
        match url {
            Ok(url) => {
                info!("url: {:#?}", url);
                info!("host: {:?}", url.host_str());
                info!("path: {:?}", url.path());
                let scheme = url.scheme();
                match scheme {
                    #[cfg(feature = "with-postgres")]
                    "postgres" => Ok(Platform::Postgres),
                    #[cfg(feature = "with-sqlite")]
                    "sqlite" => {
                        let host = url.host_str().unwrap();
                        let path = url.path();
                        let path = if path == "/" { "" } else { path };
                        let db_file = format!("{}{}", host, path);
                        Ok(Platform::Sqlite(db_file))
                    }
                    #[cfg(feature = "with-mysql")]
                    "mysql" => Ok(Platform::Mysql),
                    _ => Ok(Platform::Unsupported(scheme.to_string())),
                }
            }
            Err(e) => Err(ParseError::DbUrlParseError(e)),
        }
    }
}
<commit_msg>remove some logging<commit_after>use crate::{error::ParseError, Database};
use cfg_if::cfg_if;
use std::{convert::TryFrom, ops::Deref};
use url::Url;

cfg_if! {if #[cfg(feature = "with-postgres")]{
    use crate::pg::PostgresDB;
}}

cfg_if! {if #[cfg(feature = "with-sqlite")]{
    use crate::sqlite::SqliteDB;
}}

cfg_if! {if #[cfg(feature = "with-mysql")]{
    use crate::my::MysqlDB;
}}

pub enum DBPlatform {
    #[cfg(feature = "with-postgres")]
    Postgres(Box<PostgresDB>),
    #[cfg(feature = "with-sqlite")]
    Sqlite(Box<SqliteDB>),
    #[cfg(feature = "with-mysql")]
    Mysql(Box<MysqlDB>),
}

impl Deref for DBPlatform {
    type Target = dyn Database;

    fn deref(&self) -> &Self::Target {
        match *self {
            #[cfg(feature = "with-postgres")]
            DBPlatform::Postgres(ref pg) => pg.deref(),
            #[cfg(feature = "with-sqlite")]
            DBPlatform::Sqlite(ref sq) => sq.deref(),
            #[cfg(feature = "with-mysql")]
            DBPlatform::Mysql(ref my) => my.deref(),
        }
    }
}

impl std::ops::DerefMut for DBPlatform {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match *self {
            #[cfg(feature = "with-postgres")]
            DBPlatform::Postgres(ref mut pg) => pg.deref_mut(),
            #[cfg(feature = "with-sqlite")]
            DBPlatform::Sqlite(ref mut sq) => sq.deref_mut(),
            #[cfg(feature = "with-mysql")]
            DBPlatform::Mysql(ref mut my) => my.deref_mut(),
        }
    }
}

pub(crate) enum Platform {
    #[cfg(feature = "with-postgres")]
    Postgres,
    #[cfg(feature = "with-sqlite")]
    Sqlite(String),
    #[cfg(feature = "with-mysql")]
    Mysql,
    Unsupported(String),
}

impl<'a> TryFrom<&'a str> for Platform {
    type Error = ParseError;

    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
        let url = Url::parse(s);
        match url {
            Ok(url) => {
                let scheme = url.scheme();
                match scheme {
                    #[cfg(feature = "with-postgres")]
                    "postgres" => Ok(Platform::Postgres),
                    #[cfg(feature = "with-sqlite")]
                    "sqlite" => {
                        let host = url.host_str().unwrap();
                        let path = url.path();
                        let path = if path == "/" { "" } else { path };
                        let db_file = format!("{}{}", host, path);
                        Ok(Platform::Sqlite(db_file))
                    }
                    #[cfg(feature = "with-mysql")]
                    "mysql" => Ok(Platform::Mysql),
                    _ => Ok(Platform::Unsupported(scheme.to_string())),
                }
            }
            Err(e) => Err(ParseError::DbUrlParseError(e)),
        }
    }
}
<|endoftext|>


<commit_before>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "50171c310cd15e1b2d3723766ce64e2e4d6696fc";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // TODO: negative literal in const generic arg
    // f::<-1>()
    "src/test/debuginfo/function-names.rs",

    // TODO: unnamed fields, anonymous structs/unions
    // struct S { _: union { i: i32 } }
    "src/test/pretty/anonymous-types.rs",

    // TODO: destructuring assignment of tuple variant
    // let <E>::V {} = E::V();
    "src/test/ui/associated-types/associated-type-destructuring-assignment.rs",
    "src/test/ui/associated-types/associated-type-struct-construction.rs",

    // TODO: let else
    // let Some(1) = Some(2) else { return };
    "src/test/ui/let-else/let-else-run-pass.rs",
    "src/tools/rustfmt/tests/source/let_else.rs",
    "src/tools/rustfmt/tests/target/let_else.rs",

    // TODO: ~const
    "src/test/ui/rfc-2632-const-trait-impl/call-generic-in-impl.rs",
    "src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs",
    "src/test/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs",
    "src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs",
    "src/test/ui/rfc-2632-const-trait-impl/syntax.rs",
    "src/test/ui/rfc-2632-const-trait-impl/tilde-const-syntax.rs",
    "src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-run.rs",
    "src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs",

    // TODO: precedence of attribute on assignment stmt
    "src/tools/rustfmt/tests/source/attrib.rs",
    "src/tools/rustfmt/tests/target/attrib.rs",

    // TODO: losing empty where-clause
    "src/tools/rustfmt/tests/source/issue-3639.rs",

    // TODO: doc comment on match arm
    "src/tools/rustfmt/tests/source/match.rs",
    "src/tools/rustfmt/tests/target/match.rs",

    // TODO: inner attribute inside block
    "src/tools/rustfmt/tests/target/attrib-block-expr.rs",

    // Rustc loses some attributes
    // https://github.com/rust-lang/rust/issues/84879
    "src/test/ui/proc-macro/issue-81555.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/early/closing-args-token.rs",
    "src/test/ui/const-generics/early/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",
    "src/tools/rustfmt/tests/source/trait.rs",
    "src/tools/rustfmt/tests/target/trait.rs",

    // Excessive nesting
    "src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs",

    // Testing rustfmt on invalid syntax
    "src/tools/rustfmt/tests/coverage/target/comments.rs",
    "src/tools/rustfmt/tests/parser/issue-4126/invalid.rs",
    "src/tools/rustfmt/tests/parser/issue_4418.rs",
    "src/tools/rustfmt/tests/parser/unclosed-delims/issue_4466.rs",
    "src/tools/rustfmt/tests/source/configs/disable_all_formatting/true.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/source/type.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/target/type.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
    "src/test/ui/parser/auxiliary/issue-21146-inc.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Fill in issue links for unimplemented syntax<commit_after>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "50171c310cd15e1b2d3723766ce64e2e4d6696fc";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // TODO: negative literal in const generic arg
    // f::<-1>()
    "src/test/debuginfo/function-names.rs",

    // TODO: unnamed fields, anonymous structs/unions
    // struct S { _: union { i: i32 } }
    // https://github.com/dtolnay/syn/issues/1048
    // https://github.com/dtolnay/syn/issues/1049
    "src/test/pretty/anonymous-types.rs",

    // TODO: destructuring assignment of tuple variant
    // let <E>::V {} = E::V();
    "src/test/ui/associated-types/associated-type-destructuring-assignment.rs",
    "src/test/ui/associated-types/associated-type-struct-construction.rs",

    // TODO: let else
    // let Some(1) = Some(2) else { return };
    // https://github.com/dtolnay/syn/issues/1050
    "src/test/ui/let-else/let-else-run-pass.rs",
    "src/tools/rustfmt/tests/source/let_else.rs",
    "src/tools/rustfmt/tests/target/let_else.rs",

    // TODO: ~const
    // https://github.com/dtolnay/syn/issues/1051
    "src/test/ui/rfc-2632-const-trait-impl/call-generic-in-impl.rs",
    "src/test/ui/rfc-2632-const-trait-impl/call-generic-method-chain.rs",
    "src/test/ui/rfc-2632-const-trait-impl/call-generic-method-dup-bound.rs",
    "src/test/ui/rfc-2632-const-trait-impl/call-generic-method-pass.rs",
    "src/test/ui/rfc-2632-const-trait-impl/syntax.rs",
    "src/test/ui/rfc-2632-const-trait-impl/tilde-const-syntax.rs",
    "src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-run.rs",
    "src/test/ui/rfc-2632-const-trait-impl/trait-where-clause-self-referential.rs",

    // TODO: precedence of attribute on assignment stmt
    "src/tools/rustfmt/tests/source/attrib.rs",
    "src/tools/rustfmt/tests/target/attrib.rs",

    // TODO: losing empty where-clause
    "src/tools/rustfmt/tests/source/issue-3639.rs",

    // TODO: doc comment on match arm
    "src/tools/rustfmt/tests/source/match.rs",
    "src/tools/rustfmt/tests/target/match.rs",

    // TODO: inner attribute inside block
    "src/tools/rustfmt/tests/target/attrib-block-expr.rs",

    // Rustc loses some attributes
    // https://github.com/rust-lang/rust/issues/84879
    "src/test/ui/proc-macro/issue-81555.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/early/closing-args-token.rs",
    "src/test/ui/const-generics/early/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",
    "src/tools/rustfmt/tests/source/trait.rs",
    "src/tools/rustfmt/tests/target/trait.rs",

    // Excessive nesting
    "src/test/ui/issues/issue-74564-if-expr-stack-overflow.rs",

    // Testing rustfmt on invalid syntax
    "src/tools/rustfmt/tests/coverage/target/comments.rs",
    "src/tools/rustfmt/tests/parser/issue-4126/invalid.rs",
    "src/tools/rustfmt/tests/parser/issue_4418.rs",
    "src/tools/rustfmt/tests/parser/unclosed-delims/issue_4466.rs",
    "src/tools/rustfmt/tests/source/configs/disable_all_formatting/true.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/source/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/source/type.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/false.rs",
    "src/tools/rustfmt/tests/target/configs/spaces_around_ranges/true.rs",
    "src/tools/rustfmt/tests/target/type.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
    "src/test/ui/parser/auxiliary/issue-21146-inc.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<|endoftext|>


<commit_before>// // This Source Code Form is subject to the terms of the Mozilla Public
// // License, v. 2.0. If a copy of the MPL was not distributed with this
// // file, You can obtain one at http://mozilla.org/MPL/2.0/.

use crate::LigError;
use gaze::steps::{ignore_all, take_string, take_while_str, NoMatch};
use gaze::Gaze;
use hex::decode;
use ligature::{validate_identifier_characters, Attribute, Entity, Statement, Value};

#[derive(Debug, Clone, Copy)]
pub enum LigToken {
    OpenAngle,
    CloseAngle,
    AtSign,
    Whitespace,
    NewLine,
    DoubleQuote,
    Dot,
    Identifier,
    Integer,
}

/// Reads an Entity from the given &str.
/// Will return an error if there is anything other than an Entity + whitespace in the input.
pub fn read_entity(input: &str) -> Result<Entity, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&entity_step)
        .map_err(|_| LigError("Could not read Entity.".into()))
}

pub fn read_attribute(input: &str) -> Result<Attribute, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&attribute_step)
        .map_err(|_| LigError("Could not read Attribute.".into()))
}

/// Reads a value from a passed str.
/// Ignores white space but will return an Err if there is any input besides an encoded value.
pub fn read_value(input: &str) -> Result<Value, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&value_step)
        .map_err(|_| LigError("Could not read Value.".into()))
}

pub fn read(input: &str) -> Result<Vec<Statement>, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input);
    let mut result: Vec<Statement> = Vec::new();
    while !gaze.is_complete() {
        gaze.ignore(&ws_step);
        let entity = gaze
            .attempt(&entity_step)
            .map_err(|_| LigError("Error reading Entity.".into()))?;
        gaze.ignore(&ws_step);
        let attribute = gaze
            .attempt(&attribute_step)
            .map_err(|_| LigError("Error reading Attribute.".into()))?;
        gaze.ignore(&ws_step);
        let value = gaze
            .attempt(&value_step)
            .map_err(|_| LigError("Error reading Value.".into()))?;
        gaze.ignore(&ws_step);
        let context = gaze
            .attempt(&entity_step)
            .map_err(|_| LigError("Error reading Context.".into()))?;
        gaze.ignore(&ws_step);
        gaze.ignore(&take_string("\n"));
        result.push(Statement {
            entity,
            attribute,
            value,
            context,
        });
    }
    Ok(result)
}

fn entity_step(gaze: &mut Gaze<&str>) -> Result<Entity, NoMatch> {
    gaze.attempt(&take_string("<"))?; //.map_err(|_| LigError("Could not read Entity.".into()))?;
    let res = gaze.attempt(&take_while_str(&|c: &str| {
        validate_identifier_characters(c)
    }))?;
    gaze.attempt(&take_string(">"))?; //.map_err(|_| LigError("Could not read Entity.".into()))?;
    Ok(Entity::new(&res).map_err(|_| NoMatch)?)
}

fn attribute_step(gaze: &mut Gaze<&str>) -> Result<Attribute, NoMatch> {
    gaze.attempt(&take_string("@<"))?; //.map_err(|_| LigError("Could not read Attribute.".into()))?;
    let res = gaze.attempt(&take_while_str(&|c: &str| {
        validate_identifier_characters(c)
    }))?;
    gaze.attempt(&take_string(">"))?; //.map_err(|_| LigError("Could not read Attribute.".into()))?;
    Ok(Attribute::new(&res).map_err(|_| NoMatch)?)
}

fn value_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    if let Ok(entity) = gaze.attempt(&entity_step) {
        return Ok(Value::Entity(entity));
    }
    if let Ok(string) = gaze.attempt(&string_step) {
        return Ok(string);
    }
    if let Ok(hex) = gaze.attempt(&bytes_step) {
        return Ok(hex);
    }
    if let Ok(number) = gaze.attempt(&number_step) {
        return Ok(number);
    }
    Err(NoMatch) //Err(LigError("Could not match Value".into()))
}

fn is_digit(s: &str) -> bool {
    s == "0"
        || s == "1"
        || s == "2"
        || s == "3"
        || s == "4"
        || s == "5"
        || s == "6"
        || s == "7"
        || s == "8"
        || s == "9"
}

fn is_hex(s: &str) -> bool {
    s == "0"
        || s == "1"
        || s == "2"
        || s == "3"
        || s == "4"
        || s == "5"
        || s == "6"
        || s == "7"
        || s == "8"
        || s == "9"
        || s == "a"
        || s == "b"
        || s == "c"
        || s == "d"
        || s == "e"
        || s == "f"
}

fn ws_step(gaze: &mut Gaze<&str>) -> Result<(), NoMatch> {
    let ws: Vec<&str> = vec![" ", "\t"];
    ignore_all(ws)(gaze)
}

/// Attempts to parse an IntegerLiteral or FloatLiteral.
fn number_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    let integer = gaze.attempt(&take_while_str(&is_digit))?;
    let is_float = gaze.attempt(&take_string("."));
    match is_float {
        Ok(_) => {
            let decimal = gaze.attempt(&take_while_str(&is_digit));
            match decimal {
                Ok(decimal) => {
                    let float = format!("{}.{}", integer, decimal);
                    Ok(Value::FloatLiteral(
                        float.parse::<f64>().map_err(|_| NoMatch)?,
                    ))
                }
                Err(_) => Err(NoMatch),
            }
        }
        Err(_) => Ok(Value::IntegerLiteral(
            integer.parse::<i64>().map_err(|_| NoMatch)?,
        )),
    }
}

fn bytes_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    gaze.attempt(&take_string("0x"))?;
    let content = gaze.attempt(&take_while_str(&is_hex));
    match content {
        Ok(content) => {
            let res = decode(content).map_err(|_| NoMatch)?;
            Ok(Value::BytesLiteral(res))
        }
        Err(_) => Ok(Value::BytesLiteral(vec![])),
    }
}

fn string_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    //TODO this doesn't handle escaping
    gaze.attempt(&take_string("\""))?;
    let content = gaze.attempt(&take_while_str(&|c| c != "\""));
    gaze.attempt(&take_string("\""))?;
    match content {
        Ok(content) => Ok(Value::StringLiteral(content)),
        Err(_) => Ok(Value::StringLiteral("".into())),
    }
}

fn statement_step(gaze: &mut Gaze<&str>) -> Result<Statement, NoMatch> {
    todo!()
}
<commit_msg>removing LigToken<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use crate::LigError;
use gaze::steps::{ignore_all, take_string, take_while_str, NoMatch};
use gaze::Gaze;
use hex::decode;
use ligature::{validate_identifier_characters, Attribute, Entity, Statement, Value};

/// Reads an Entity from the given &str.
/// Will return an error if there is anything other than an Entity + whitespace in the input.
pub fn read_entity(input: &str) -> Result<Entity, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&entity_step)
        .map_err(|_| LigError("Could not read Entity.".into()))
}

pub fn read_attribute(input: &str) -> Result<Attribute, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&attribute_step)
        .map_err(|_| LigError("Could not read Attribute.".into()))
}

/// Reads a value from a passed str.
/// Ignores white space but will return an Err if there is any input besides an encoded value.
pub fn read_value(input: &str) -> Result<Value, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input.trim());
    gaze.attempt(&value_step)
        .map_err(|_| LigError("Could not read Value.".into()))
}

pub fn read(input: &str) -> Result<Vec<Statement>, LigError> {
    let mut gaze = Gaze::<&str>::from_str(input);
    let mut result: Vec<Statement> = Vec::new();
    while !gaze.is_complete() {
        gaze.ignore(&ws_step);
        let entity = gaze
            .attempt(&entity_step)
            .map_err(|_| LigError("Error reading Entity.".into()))?;
        gaze.ignore(&ws_step);
        let attribute = gaze
            .attempt(&attribute_step)
            .map_err(|_| LigError("Error reading Attribute.".into()))?;
        gaze.ignore(&ws_step);
        let value = gaze
            .attempt(&value_step)
            .map_err(|_| LigError("Error reading Value.".into()))?;
        gaze.ignore(&ws_step);
        let context = gaze
            .attempt(&entity_step)
            .map_err(|_| LigError("Error reading Context.".into()))?;
        gaze.ignore(&ws_step);
        gaze.ignore(&take_string("\n"));
        result.push(Statement {
            entity,
            attribute,
            value,
            context,
        });
    }
    Ok(result)
}

fn entity_step(gaze: &mut Gaze<&str>) -> Result<Entity, NoMatch> {
    gaze.attempt(&take_string("<"))?; //.map_err(|_| LigError("Could not read Entity.".into()))?;
    let res = gaze.attempt(&take_while_str(&|c: &str| {
        validate_identifier_characters(c)
    }))?;
    gaze.attempt(&take_string(">"))?; //.map_err(|_| LigError("Could not read Entity.".into()))?;
    Ok(Entity::new(&res).map_err(|_| NoMatch)?)
}

fn attribute_step(gaze: &mut Gaze<&str>) -> Result<Attribute, NoMatch> {
    gaze.attempt(&take_string("@<"))?; //.map_err(|_| LigError("Could not read Attribute.".into()))?;
    let res = gaze.attempt(&take_while_str(&|c: &str| {
        validate_identifier_characters(c)
    }))?;
    gaze.attempt(&take_string(">"))?; //.map_err(|_| LigError("Could not read Attribute.".into()))?;
    Ok(Attribute::new(&res).map_err(|_| NoMatch)?)
}

fn value_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    if let Ok(entity) = gaze.attempt(&entity_step) {
        return Ok(Value::Entity(entity));
    }
    if let Ok(string) = gaze.attempt(&string_step) {
        return Ok(string);
    }
    if let Ok(hex) = gaze.attempt(&bytes_step) {
        return Ok(hex);
    }
    if let Ok(number) = gaze.attempt(&number_step) {
        return Ok(number);
    }
    Err(NoMatch) //Err(LigError("Could not match Value".into()))
}

fn is_digit(s: &str) -> bool {
    s == "0"
        || s == "1"
        || s == "2"
        || s == "3"
        || s == "4"
        || s == "5"
        || s == "6"
        || s == "7"
        || s == "8"
        || s == "9"
}

fn is_hex(s: &str) -> bool {
    s == "0"
        || s == "1"
        || s == "2"
        || s == "3"
        || s == "4"
        || s == "5"
        || s == "6"
        || s == "7"
        || s == "8"
        || s == "9"
        || s == "a"
        || s == "b"
        || s == "c"
        || s == "d"
        || s == "e"
        || s == "f"
}

fn ws_step(gaze: &mut Gaze<&str>) -> Result<(), NoMatch> {
    let ws: Vec<&str> = vec![" ", "\t"];
    ignore_all(ws)(gaze)
}

/// Attempts to parse an IntegerLiteral or FloatLiteral.
fn number_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    let integer = gaze.attempt(&take_while_str(&is_digit))?;
    let is_float = gaze.attempt(&take_string("."));
    match is_float {
        Ok(_) => {
            let decimal = gaze.attempt(&take_while_str(&is_digit));
            match decimal {
                Ok(decimal) => {
                    let float = format!("{}.{}", integer, decimal);
                    Ok(Value::FloatLiteral(
                        float.parse::<f64>().map_err(|_| NoMatch)?,
                    ))
                }
                Err(_) => Err(NoMatch),
            }
        }
        Err(_) => Ok(Value::IntegerLiteral(
            integer.parse::<i64>().map_err(|_| NoMatch)?,
        )),
    }
}

fn bytes_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    gaze.attempt(&take_string("0x"))?;
    let content = gaze.attempt(&take_while_str(&is_hex));
    match content {
        Ok(content) => {
            let res = decode(content).map_err(|_| NoMatch)?;
            Ok(Value::BytesLiteral(res))
        }
        Err(_) => Ok(Value::BytesLiteral(vec![])),
    }
}

fn string_step(gaze: &mut Gaze<&str>) -> Result<Value, NoMatch> {
    //TODO this doesn't handle escaping
    gaze.attempt(&take_string("\""))?;
    let content = gaze.attempt(&take_while_str(&|c| c != "\""));
    gaze.attempt(&take_string("\""))?;
    match content {
        Ok(content) => Ok(Value::StringLiteral(content)),
        Err(_) => Ok(Value::StringLiteral("".into())),
    }
}

fn statement_step(gaze: &mut Gaze<&str>) -> Result<Statement, NoMatch> {
    todo!()
}
<|endoftext|>


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

/// A shader module.
///
/// A shader module is a container that associates some shading code to several identifiers.
struct ShaderModule {
  symbols: HashMap<Identifier, ShadingCode>
}

/// Spectra Shading Language AST.
#[derive(Clone, Debug, Eq, PartialEq)]
enum SSL {
  /// An `export list_of_identifiers_` statement.
  Export(ExportList),
  /// A `from module import list of identifiers` statement.
  FromUse(ImportList),
  /// A `pipeline { list_of_pipeline_attributes }` statement.
  Pipeline(PipelineStatement),
  /// A yield statement, valid in geometry shaders.
  Yield(GeometryYieldExpression),
}

/// A module.
type ModuleName = String;
/// An identifier.
pub type Identifier = String;
/// Some opaque shading code.
type ShadingCode = String;
/// An expression.
type Expression = String;

/// An export non-empty list.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExportList {
  pub export_list: Vec<ModulePath>
}

/// An import non-empty list.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ImportList {
  pub module: ModuleName,
  pub list: Vec<ModulePath>
}

/// A module path is a list of module(s), representing a hierarchy.
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct ModulePath {
  pub path: Vec<ModuleName>
}

/// A pipeline statement.
#[derive(Clone, Debug, Eq, PartialEq)]
struct PipelineStatement {
  attributes: Vec<PipelineAttribute>
}

/// Attributes that can be set in a pipeline.
#[derive(Clone, Debug, Eq, PartialEq)]
enum PipelineAttribute {
  /// Maximum vertices that the geometry shader can output.
  GeometryShaderMaxVertices(u32),
  /// Number of times the geometry shader must be invoked.
  GeometryShaderInvokations(u32)
}

/// Expressions that can be yielded in a geometry shader.
#[derive(Clone, Debug, Eq, PartialEq)]
enum GeometryYieldExpression {
  /// Yield a primitive.
  YieldPrimitive,
  /// Yield a primitive’s vertex (fold vertex).
  YieldFoldVertex(Expression)
}

/// Error that can occur when parsing SSL code.
#[derive(Clone, Debug, Eq, PartialEq)]
enum ParseError {
  ExpressionError(String)
}
<commit_msg>FromUse -> Import. #24<commit_after>use std::collections::HashMap;

/// A shader module.
///
/// A shader module is a container that associates some shading code to several identifiers.
struct ShaderModule {
  symbols: HashMap<Identifier, ShadingCode>
}

/// Spectra Shading Language AST.
#[derive(Clone, Debug, Eq, PartialEq)]
enum SSL {
  /// An `export list_of_identifiers_` statement.
  Export(ExportList),
  /// A `from module import list of identifiers` statement.
  Import(ImportList),
  /// A `pipeline { list_of_pipeline_attributes }` statement.
  Pipeline(PipelineStatement),
  /// A yield statement, valid in geometry shaders.
  Yield(GeometryYieldExpression),
}

/// A module.
type ModuleName = String;
/// An identifier.
pub type Identifier = String;
/// Some opaque shading code.
type ShadingCode = String;
/// An expression.
type Expression = String;

/// An export non-empty list.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ExportList {
  pub export_list: Vec<ModulePath>
}

/// An import non-empty list.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ImportList {
  pub module: ModuleName,
  pub list: Vec<ModulePath>
}

/// A module path is a list of module(s), representing a hierarchy.
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct ModulePath {
  pub path: Vec<ModuleName>
}

/// A pipeline statement.
#[derive(Clone, Debug, Eq, PartialEq)]
struct PipelineStatement {
  attributes: Vec<PipelineAttribute>
}

/// Attributes that can be set in a pipeline.
#[derive(Clone, Debug, Eq, PartialEq)]
enum PipelineAttribute {
  /// Maximum vertices that the geometry shader can output.
  GeometryShaderMaxVertices(u32),
  /// Number of times the geometry shader must be invoked.
  GeometryShaderInvokations(u32)
}

/// Expressions that can be yielded in a geometry shader.
#[derive(Clone, Debug, Eq, PartialEq)]
enum GeometryYieldExpression {
  /// Yield a primitive.
  YieldPrimitive,
  /// Yield a primitive’s vertex (fold vertex).
  YieldFoldVertex(Expression)
}

/// Error that can occur when parsing SSL code.
#[derive(Clone, Debug, Eq, PartialEq)]
enum ParseError {
  ExpressionError(String)
}
<|endoftext|>