<commit_before>use {Value, Response};
use parser::parse_response;
use error::RequestError;
use utils::escape_xml;

use hyper::client::{Client, Body};
use hyper::header::{ContentType, UserAgent};

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

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<(&'a str, Value)>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name: name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, name: &'a str, value: T) -> Self {
        self.args.push((name, value.into()));

        Request {
            name: self.name,
            args: self.args,
        }
    }

    /// Calls the method using the given `Client`.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(&self, client: &Client, url: &str) -> RequestResult {
        use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

        // First, build the body XML
        let mut body = Vec::new();
        try!(self.write_as_xml(&mut body));

        // Send XML-RPC request
        let mut response = try!(client.post(url)
            .header(UserAgent("Rust xmlrpc".to_string()))
            .header(ContentType(Mime(TopLevel::Text, SubLevel::Xml, vec![(Attr::Charset, Value::Utf8)])))
            .body(Body::BufBody(&body, body.len()))
            .send());

        // FIXME Check that the response headers are correct

        // Read the response and parse it
        // FIXME `BufRead`?
        Ok(try!(parse_response(&mut response)))
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        try!(write!(fmt, r#"<?xml version="1.1" encoding="utf-8"?>"#));
        try!(write!(fmt, r#"<methodCall>"#));
        try!(write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name)));
        try!(write!(fmt, r#"    <params>"#));
        for &(_, ref value) in &self.args {
            try!(write!(fmt, r#"        <param>"#));
            try!(value.format(fmt));
            try!(write!(fmt, r#"        </param>"#));
        }
        try!(write!(fmt, r#"    </params>"#));
        try!(write!(fmt, r#"</methodCall>"#));
        Ok(())
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<commit_msg>Remove (unused) argument names<commit_after>use {Value, Response};
use parser::parse_response;
use error::RequestError;
use utils::escape_xml;

use hyper::client::{Client, Body};
use hyper::header::{ContentType, UserAgent};

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

/// A request to call a procedure.
pub struct Request<'a> {
    name: &'a str,
    args: Vec<Value>,
}

impl<'a> Request<'a> {
    /// Creates a new request to call a function named `name`.
    ///
    /// By default, no arguments are passed. Use the `arg` method to append arguments.
    pub fn new(name: &'a str) -> Self {
        Request {
            name: name,
            args: Vec::new(),
        }
    }

    /// Appends an argument to be passed to the current list of arguments.
    pub fn arg<T: Into<Value>>(mut self, value: T) -> Self {
        self.args.push(value.into());

        Request {
            name: self.name,
            args: self.args,
        }
    }

    /// Calls the method using the given `Client`.
    ///
    /// Returns a `RequestResult` indicating whether the request was sent and processed successfully
    /// (according to the rules of XML-RPC).
    pub fn call(&self, client: &Client, url: &str) -> RequestResult {
        use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

        // First, build the body XML
        let mut body = Vec::new();
        try!(self.write_as_xml(&mut body));

        // Send XML-RPC request
        let mut response = try!(client.post(url)
            .header(UserAgent("Rust xmlrpc".to_string()))
            .header(ContentType(Mime(TopLevel::Text, SubLevel::Xml, vec![(Attr::Charset, Value::Utf8)])))
            .body(Body::BufBody(&body, body.len()))
            .send());

        // FIXME Check that the response headers are correct

        // Read the response and parse it
        // FIXME `BufRead`?
        Ok(try!(parse_response(&mut response)))
    }

    /// Formats this `Request` as XML.
    pub fn write_as_xml<W: Write>(&self, fmt: &mut W) -> io::Result<()> {
        try!(write!(fmt, r#"<?xml version="1.1" encoding="utf-8"?>"#));
        try!(write!(fmt, r#"<methodCall>"#));
        try!(write!(fmt, r#"    <methodName>{}</methodName>"#, escape_xml(&self.name)));
        try!(write!(fmt, r#"    <params>"#));
        for value in &self.args {
            try!(write!(fmt, r#"        <param>"#));
            try!(value.format(fmt));
            try!(write!(fmt, r#"        </param>"#));
        }
        try!(write!(fmt, r#"    </params>"#));
        try!(write!(fmt, r#"</methodCall>"#));
        Ok(())
    }
}

/// The result of executing a request.
///
/// When the request was executed without major errors (like an HTTP error or a malformed response),
/// this is `Ok`. The `Response` can still denote a `Fault` if the server returned a `<fault>`
/// response.
pub type RequestResult = Result<Response, RequestError>;

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

    #[test]
    fn escapes_method_names() {
        let mut output: Vec<u8> = Vec::new();
        let req = Request::new("x<&x");

        req.write_as_xml(&mut output).unwrap();
        assert!(
            str::from_utf8(&output)
            .unwrap()
            .contains("<methodName>x<&x</methodName>"));
    }
}
<|endoftext|>


<commit_before>extern crate syntax;

pub use syntax::ast;
pub use syntax::ptr::P;
pub use syntax::codemap::DUMMY_SP;
pub use syntax::ext::base::ExtCtxt;
pub use syntax::ast::{Mod, Item, Expr, ViewItem};
pub use syntax::parse::token::str_to_ident;
pub use syntax::ext::build::AstBuilder;
pub use syntax::print::pprust::{expr_to_string, item_to_string};
use syntax::print::pprust::to_string;

pub fn module(view_items: Vec<ViewItem>, items: Vec<P<Item>>) -> P<Mod> {
	P(Mod{
		inner: DUMMY_SP,
		view_items: view_items,
		items: items,
	})
}

pub fn parse_path(e: &str) -> ast::Path {
	let ps = syntax::parse::new_parse_sess();
	let mut p = syntax::parse::new_parser_from_source_str(&ps, Vec::new(), "file".to_string(), e.to_string());
	let r = p.parse_path(syntax::parse::parser::NoTypesAllowed);
	p.abort_if_errors();
	r.path
}

pub fn parse_path_vec(s: &str) -> Vec<ast::Ident> {
	s.split_str("::").map(|i| str_to_ident(i)).collect()
}

pub fn parse_block(e: &str) -> P<ast::Block> {
	let ps = syntax::parse::new_parse_sess();
	let mut p = syntax::parse::new_parser_from_source_str(&ps, Vec::new(), "file".to_string(), e.to_string());
	let r = p.parse_block();
	p.abort_if_errors();
	r
}

pub fn parse_type(e: &str) -> P<ast::Ty> {
	let ps = syntax::parse::new_parse_sess();
	let mut p = syntax::parse::new_parser_from_source_str(&ps, Vec::new(), "file".to_string(), e.to_string());
	let r = p.parse_ty(false);
	p.abort_if_errors();
	r
}

// FIXME: https://github.com/rust-lang/rust/pull/18256
pub fn view_item_to_string(i: &ast::ViewItem) -> String {
    to_string(|s| s.print_view_item(i))
}
<commit_msg>rust: use new view_item_to_string<commit_after>extern crate syntax;

pub use syntax::ast;
pub use syntax::ptr::P;
pub use syntax::codemap::DUMMY_SP;
pub use syntax::ext::base::ExtCtxt;
pub use syntax::ast::{Mod, Item, Expr, ViewItem};
pub use syntax::parse::token::str_to_ident;
pub use syntax::ext::build::AstBuilder;
pub use syntax::print::pprust::{expr_to_string, item_to_string, view_item_to_string};

pub fn module(view_items: Vec<ViewItem>, items: Vec<P<Item>>) -> P<Mod> {
	P(Mod{
		inner: DUMMY_SP,
		view_items: view_items,
		items: items,
	})
}

pub fn parse_path(e: &str) -> ast::Path {
	let ps = syntax::parse::new_parse_sess();
	let mut p = syntax::parse::new_parser_from_source_str(&ps, Vec::new(), "file".to_string(), e.to_string());
	let r = p.parse_path(syntax::parse::parser::NoTypesAllowed);
	p.abort_if_errors();
	r.path
}

pub fn parse_path_vec(s: &str) -> Vec<ast::Ident> {
	s.split_str("::").map(|i| str_to_ident(i)).collect()
}

pub fn parse_block(e: &str) -> P<ast::Block> {
	let ps = syntax::parse::new_parse_sess();
	let mut p = syntax::parse::new_parser_from_source_str(&ps, Vec::new(), "file".to_string(), e.to_string());
	let r = p.parse_block();
	p.abort_if_errors();
	r
}

pub fn parse_type(e: &str) -> P<ast::Ty> {
	let ps = syntax::parse::new_parse_sess();
	let mut p = syntax::parse::new_parser_from_source_str(&ps, Vec::new(), "file".to_string(), e.to_string());
	let r = p.parse_ty(false);
	p.abort_if_errors();
	r
}
<|endoftext|>


<commit_before>// Copyright 2016 Dmitry "Divius" Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

//! Protocol-agnostic service implementation

use std::collections::HashMap;

use num;

use super::{GenericNodeTable, Node};

static MAX_NODE_COUNT: usize = 16;

/// Service - generic implementation of DHT calls.
pub struct Service<TNodeTable, TData>
        where TNodeTable: GenericNodeTable, TData: Clone {
    table: TNodeTable,
    node_id: num::BigUint,
    clean_needed: bool,
    data: HashMap<num::BigUint, TData>
}

#[derive(Debug)]
pub enum FindResult<TData> {
    Value(TData),
    ClosestNodes(Vec<Node>),
    Nothing
}

impl<TNodeTable, TData> Service<TNodeTable, TData>
        where TNodeTable: GenericNodeTable, TData: Clone {
    /// Create a service with a random ID.
    pub fn new(node_table: TNodeTable) -> Service<TNodeTable, TData> {
        let node_id = node_table.random_id();
        Service::new_with_id(node_table, node_id)
    }
    /// Create a service with a given ID.
    pub fn new_with_id(node_table: TNodeTable, node_id: num::BigUint)
            -> Service<TNodeTable, TData> {
        Service {
            table: node_table,
            node_id: node_id,
            clean_needed: false,
            data: HashMap::new()
        }
    }

    /// Get an immutable reference to the node table.
    pub fn node_table(&self) -> &TNodeTable { &self.table }
    /// Get a mutable reference to the node table.
    pub fn node_table_mut(&mut self) -> &mut TNodeTable { &mut self.table }
    /// Get the current node ID.
    pub fn node_id(&self) -> &num::BigUint { &self.node_id }
    /// Get an immutable reference to the data.
    pub fn stored_data(&self) -> &HashMap<num::BigUint, TData> { &self.data }
    /// Get an immutable reference to the data.
    pub fn stored_data_mut(&mut self) -> &mut HashMap<num::BigUint, TData> {
        &mut self.data
    }
    /// Check if some buckets are full already.
    pub fn clean_needed(&self) -> bool { self.clean_needed }

    /// Process the ping request.
    ///
    /// Essentially remembers the incoming node and returns true.
    pub fn ping(&mut self, sender: &Node) -> bool {
        self.update(sender);
        true
    }
    /// Process the find request.
    pub fn find_node(&mut self, sender: &Node, id: &num::BigUint) -> Vec<Node> {
        let res = self.table.find(&id, MAX_NODE_COUNT);
        self.update(sender);
        res
    }
    /// Find a value or the closes nodes.
    pub fn find_value(&mut self, sender: &Node, id: &num::BigUint)
            -> FindResult<&TData> {
        self.update(sender);
        let res = match self.data.get(&id) {
            Some(value) => FindResult::Value(value),
            None => FindResult::ClosestNodes(self.table.find(&id, MAX_NODE_COUNT))
        };
        res
    }

    /// Try to clean up the table by checking the oldest records.
    ///
    /// Should be called periodically, especially when clean_needed is true.
    pub fn clean_up<TCheck>(&mut self, mut check: TCheck)
            where TCheck: FnMut(&Node) -> bool {
        let oldest = self.table.pop_oldest();
        for node in oldest {
            if check(&node) {
                self.table.update(&node);
            }
        }
        self.clean_needed = false;
    }

    fn update(&mut self, node: &Node) {
        if node.id == self.node_id {
            return
        }

        if ! self.table.update(&node) {
            self.clean_needed = true;
        }
    }
}

impl<TNodeTable> Service<TNodeTable, String>
        where TNodeTable: GenericNodeTable {
    /// Create a service with random ID and string data.
    pub fn new_with_strings(node_table: TNodeTable)
            -> Service<TNodeTable, String> {
        Service::new(node_table)
    }
}

#[cfg(test)]
pub mod test {
    use num::{self, FromPrimitive, ToPrimitive};

    use super::super::{GenericNodeTable, Node};
    use super::super::utils::test;

    use super::{Service, FindResult};

    struct DummyNodeTable {
        pub node: Option<Node>
    }

    impl GenericNodeTable for DummyNodeTable {
        fn random_id(&self) -> num::BigUint {
            FromPrimitive::from_usize(42).unwrap()
        }

        fn update(&mut self, node: &Node) -> bool {
            match self.node {
                Some(..) => false,
                None => {
                    self.node = Some(node.clone());
                    true
                }
            }
        }

        fn find(&self, id: &num::BigUint, _count: usize) -> Vec<Node> {
            if let Some(ref node) = self.node {
                if node.id == *id {
                    vec![node.clone()]
                }
                else {
                    vec![]
                }
            }
            else {
                vec![]
            }
        }

        fn pop_oldest(&mut self) -> Vec<Node> {
            let result;
            if let Some(ref node) = self.node {
                result = vec![node.clone()];
            }
            else {
                result = vec![];
            }
            self.node = None;
            result
        }
    }

    #[test]
    fn test_new() {
        let node_table = DummyNodeTable { node: None };
        let mut svc: Service<DummyNodeTable, String> =
            Service::new_with_strings(node_table);

        assert_eq!(42, svc.node_id().to_i8().unwrap());
        assert!(svc.node_table().node.is_none());
        assert!(svc.node_table_mut().node.is_none());
        assert!(!svc.clean_needed());
    }

    #[test]
    fn test_find_saves_node() {
        let node_table = DummyNodeTable { node: None };
        let mut svc: Service<DummyNodeTable, String> =
            Service::new_with_strings(node_table);
        let node = test::new_node(43);

        assert!(svc.find_node(&node, &node.id).is_empty());
        let result = svc.find_node(&node, &node.id);
        assert_eq!(1, result.len());
        assert_eq!(43, result.get(0).unwrap().id.to_i8().unwrap())
    }

    #[test]
    fn test_ping_find_clean() {
        let node_table = DummyNodeTable { node: None };
        let mut svc: Service<DummyNodeTable, String> =
            Service::new_with_strings(node_table);
        let node = test::new_node(43);

        assert!(svc.ping(&node));
        assert_eq!(43, svc.table.node.as_ref().unwrap().id.to_i8().unwrap());
        assert!(!svc.clean_needed());

        assert!(svc.ping(&test::new_node(44)));
        assert_eq!(43, svc.table.node.as_ref().unwrap().id.to_i8().unwrap());
        assert!(svc.clean_needed());

        let mut result = svc.find_node(&node, &node.id);
        assert_eq!(1, result.len());
        assert_eq!(43, result.get(0).unwrap().id.to_i8().unwrap());

        let mut flag = false;
        svc.clean_up(|node| {
            assert_eq!(43, node.id.to_i8().unwrap());
            flag = true;
            true
        });
        assert!(flag);
        assert!(!svc.clean_needed());

        result = svc.find_node(&node, &node.id);
        assert_eq!(1, result.len());
        assert_eq!(43, result.get(0).unwrap().id.to_i8().unwrap());

        flag = false;
        svc.clean_up(|node| {
            assert_eq!(43, node.id.to_i8().unwrap());
            flag = true;
            false
        });
        assert!(flag);
        assert!(!svc.clean_needed());
        assert!(svc.find_node(&node, &node.id).is_empty());
    }

    #[test]
    fn test_ping_find_value() {
        let node_table = DummyNodeTable { node: None };
        let mut svc: Service<DummyNodeTable, String> =
            Service::new_with_strings(node_table);
        let node = test::new_node(43);
        let id1: num::BigUint = FromPrimitive::from_usize(44).unwrap();
        let id2: num::BigUint = FromPrimitive::from_usize(43).unwrap();

        svc.ping(&node);
        svc.stored_data_mut().insert(id1.clone(), "foobar".to_string());

        {
            let res1 = svc.find_value(&node, &id1);
            match res1 {
                FindResult::Value(value) => assert_eq!("foobar", value),
                _ => panic!("wrong result {:?}", res1)
            }
        }

        {
            let res2 = svc.find_value(&node, &id2);
            match res2 {
                FindResult::ClosestNodes(nodes) => assert_eq!(1, nodes.len()),
                _ => panic!("wrong result {:?}", res2)
            }
        }
    }
}
<commit_msg>Prefix request handlers in Service with on_<commit_after>// Copyright 2016 Dmitry "Divius" Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

//! Protocol-agnostic service implementation

use std::collections::HashMap;

use num;

use super::{GenericNodeTable, Node};

static MAX_NODE_COUNT: usize = 16;

/// Service - generic implementation of DHT calls.
pub struct Service<TNodeTable, TData>
        where TNodeTable: GenericNodeTable, TData: Clone {
    table: TNodeTable,
    node_id: num::BigUint,
    clean_needed: bool,
    data: HashMap<num::BigUint, TData>
}

#[derive(Debug)]
pub enum FindResult<TData> {
    Value(TData),
    ClosestNodes(Vec<Node>),
    Nothing
}

impl<TNodeTable, TData> Service<TNodeTable, TData>
        where TNodeTable: GenericNodeTable, TData: Clone {
    /// Create a service with a random ID.
    pub fn new(node_table: TNodeTable) -> Service<TNodeTable, TData> {
        let node_id = node_table.random_id();
        Service::new_with_id(node_table, node_id)
    }
    /// Create a service with a given ID.
    pub fn new_with_id(node_table: TNodeTable, node_id: num::BigUint)
            -> Service<TNodeTable, TData> {
        Service {
            table: node_table,
            node_id: node_id,
            clean_needed: false,
            data: HashMap::new()
        }
    }

    /// Get an immutable reference to the node table.
    pub fn node_table(&self) -> &TNodeTable { &self.table }
    /// Get a mutable reference to the node table.
    pub fn node_table_mut(&mut self) -> &mut TNodeTable { &mut self.table }
    /// Get the current node ID.
    pub fn node_id(&self) -> &num::BigUint { &self.node_id }
    /// Get an immutable reference to the data.
    pub fn stored_data(&self) -> &HashMap<num::BigUint, TData> { &self.data }
    /// Get an immutable reference to the data.
    pub fn stored_data_mut(&mut self) -> &mut HashMap<num::BigUint, TData> {
        &mut self.data
    }
    /// Check if some buckets are full already.
    pub fn clean_needed(&self) -> bool { self.clean_needed }

    /// Process the ping request.
    ///
    /// Essentially remembers the incoming node and returns true.
    pub fn on_ping(&mut self, sender: &Node) -> bool {
        self.update(sender);
        true
    }
    /// Process the find request.
    pub fn on_find_node(&mut self, sender: &Node, id: &num::BigUint) -> Vec<Node> {
        let res = self.table.find(&id, MAX_NODE_COUNT);
        self.update(sender);
        res
    }
    /// Find a value or the closes nodes.
    pub fn on_find_value(&mut self, sender: &Node, id: &num::BigUint)
            -> FindResult<&TData> {
        self.update(sender);
        let res = match self.data.get(&id) {
            Some(value) => FindResult::Value(value),
            None => FindResult::ClosestNodes(self.table.find(&id, MAX_NODE_COUNT))
        };
        res
    }

    /// Try to clean up the table by checking the oldest records.
    ///
    /// Should be called periodically, especially when clean_needed is true.
    pub fn clean_up<TCheck>(&mut self, mut check: TCheck)
            where TCheck: FnMut(&Node) -> bool {
        let oldest = self.table.pop_oldest();
        for node in oldest {
            if check(&node) {
                self.table.update(&node);
            }
        }
        self.clean_needed = false;
    }

    fn update(&mut self, node: &Node) {
        if node.id == self.node_id {
            return
        }

        if ! self.table.update(&node) {
            self.clean_needed = true;
        }
    }
}

impl<TNodeTable> Service<TNodeTable, String>
        where TNodeTable: GenericNodeTable {
    /// Create a service with random ID and string data.
    pub fn new_with_strings(node_table: TNodeTable)
            -> Service<TNodeTable, String> {
        Service::new(node_table)
    }
}

#[cfg(test)]
pub mod test {
    use num::{self, FromPrimitive, ToPrimitive};

    use super::super::{GenericNodeTable, Node};
    use super::super::utils::test;

    use super::{Service, FindResult};

    struct DummyNodeTable {
        pub node: Option<Node>
    }

    impl GenericNodeTable for DummyNodeTable {
        fn random_id(&self) -> num::BigUint {
            FromPrimitive::from_usize(42).unwrap()
        }

        fn update(&mut self, node: &Node) -> bool {
            match self.node {
                Some(..) => false,
                None => {
                    self.node = Some(node.clone());
                    true
                }
            }
        }

        fn find(&self, id: &num::BigUint, _count: usize) -> Vec<Node> {
            if let Some(ref node) = self.node {
                if node.id == *id {
                    vec![node.clone()]
                }
                else {
                    vec![]
                }
            }
            else {
                vec![]
            }
        }

        fn pop_oldest(&mut self) -> Vec<Node> {
            let result;
            if let Some(ref node) = self.node {
                result = vec![node.clone()];
            }
            else {
                result = vec![];
            }
            self.node = None;
            result
        }
    }

    #[test]
    fn test_new() {
        let node_table = DummyNodeTable { node: None };
        let mut svc: Service<DummyNodeTable, String> =
            Service::new_with_strings(node_table);

        assert_eq!(42, svc.node_id().to_i8().unwrap());
        assert!(svc.node_table().node.is_none());
        assert!(svc.node_table_mut().node.is_none());
        assert!(!svc.clean_needed());
    }

    #[test]
    fn test_find_saves_node() {
        let node_table = DummyNodeTable { node: None };
        let mut svc: Service<DummyNodeTable, String> =
            Service::new_with_strings(node_table);
        let node = test::new_node(43);

        assert!(svc.on_find_node(&node, &node.id).is_empty());
        let result = svc.on_find_node(&node, &node.id);
        assert_eq!(1, result.len());
        assert_eq!(43, result.get(0).unwrap().id.to_i8().unwrap())
    }

    #[test]
    fn test_ping_find_clean() {
        let node_table = DummyNodeTable { node: None };
        let mut svc: Service<DummyNodeTable, String> =
            Service::new_with_strings(node_table);
        let node = test::new_node(43);

        assert!(svc.on_ping(&node));
        assert_eq!(43, svc.table.node.as_ref().unwrap().id.to_i8().unwrap());
        assert!(!svc.clean_needed());

        assert!(svc.on_ping(&test::new_node(44)));
        assert_eq!(43, svc.table.node.as_ref().unwrap().id.to_i8().unwrap());
        assert!(svc.clean_needed());

        let mut result = svc.on_find_node(&node, &node.id);
        assert_eq!(1, result.len());
        assert_eq!(43, result.get(0).unwrap().id.to_i8().unwrap());

        let mut flag = false;
        svc.clean_up(|node| {
            assert_eq!(43, node.id.to_i8().unwrap());
            flag = true;
            true
        });
        assert!(flag);
        assert!(!svc.clean_needed());

        result = svc.on_find_node(&node, &node.id);
        assert_eq!(1, result.len());
        assert_eq!(43, result.get(0).unwrap().id.to_i8().unwrap());

        flag = false;
        svc.clean_up(|node| {
            assert_eq!(43, node.id.to_i8().unwrap());
            flag = true;
            false
        });
        assert!(flag);
        assert!(!svc.clean_needed());
        assert!(svc.on_find_node(&node, &node.id).is_empty());
    }

    #[test]
    fn test_ping_find_value() {
        let node_table = DummyNodeTable { node: None };
        let mut svc: Service<DummyNodeTable, String> =
            Service::new_with_strings(node_table);
        let node = test::new_node(43);
        let id1: num::BigUint = FromPrimitive::from_usize(44).unwrap();
        let id2: num::BigUint = FromPrimitive::from_usize(43).unwrap();

        svc.on_ping(&node);
        svc.stored_data_mut().insert(id1.clone(), "foobar".to_string());

        {
            let res1 = svc.on_find_value(&node, &id1);
            match res1 {
                FindResult::Value(value) => assert_eq!("foobar", value),
                _ => panic!("wrong result {:?}", res1)
            }
        }

        {
            let res2 = svc.on_find_value(&node, &id2);
            match res2 {
                FindResult::ClosestNodes(nodes) => assert_eq!(1, nodes.len()),
                _ => panic!("wrong result {:?}", res2)
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::sync::mpsc::{channel, Sender};

use misc::{NamedStorage, SerializationFormat, Subset};
use task::{Op, PlainRawStorage, KeyedRawStorage, TelemetryTask};
use indexing::*;

///
/// The Telemetry service.
///
/// The service is in charge of maintaining the data recorded by the
/// histograms. Each application using telemetry needs one instance of
/// the service (or more, but this should seldom be necessary). The
/// data is stored and processed in a dedicated background thread and
/// the memory is recollected only when the service is dropped.
///
/// # Panics
///
/// The service will panic if an attempt is made to register two
/// histograms with the same key.
///
impl Service {
    pub fn new(is_active: bool) -> Service {
        let (sender, receiver) = channel();
        thread::spawn(|| {
            let mut task = TelemetryTask::new(receiver);
            task.run()
        });
        Service {
            keys_plain: KeyGenerator::new(),
            keys_keyed: KeyGenerator::new(),
            sender: sender,
            is_active: Arc::new(AtomicBool::new(is_active)),
        }
    }

    ///
    /// Serialize all histograms as json, in a given format.
    ///
    /// # Panics
    ///
    /// The service will panic if the sender is closed by the time serialization
    /// is complete.
    ///
    pub fn to_json(&self, what: Subset, format: SerializationFormat, sender: Sender<Json>) {
        self.sender.send(Op::Serialize(what, format, sender)).unwrap();
    }

    ///
    /// Make the service (in)active.
    ///
    /// Any data recorded on a histogram while the service is inactive will be ignored.
    ///
    pub fn set_active(&self, value: bool) {
        self.is_active.store(value, Ordering::Relaxed);
    }

    pub fn is_active(&self) -> bool {
        self.is_active.load(Ordering::Relaxed)
    }

    ///
    /// Register a plain histogram, returning a fresh key.
    ///
    fn register_plain(&self, name: String, storage: Box<PlainRawStorage>) -> Key<Plain> {
        let key = self.keys_plain.next();
        let named = NamedStorage { name: name, contents: storage };
        self.sender.send(Op::RegisterPlain(key.index, named)).unwrap();
        key
    }

    ///
    /// Register a keyed histogram, returning a fresh key.
    ///
    fn register_keyed<T>(&self, name: String, storage: Box<KeyedRawStorage>) -> Key<Keyed<T>> {
        let key = self.keys_keyed.next();
        let named = NamedStorage { name: name, contents: storage };
        self.sender.send(Op::RegisterKeyed(key.index, named)).unwrap();
        key
    }
}

/// Upon death of the service, terminate the thread and recollect all
/// owned memory.
impl Drop for Service {
    fn drop(&mut self) {
        let _ = self.sender.send(Op::Terminate);
    }
}

pub struct Service {
    /// A key generator for registration of new plain histograms. Uses
    /// atomic to avoid the use of &mut.
    keys_plain: KeyGenerator<Plain>,

    /// A key generator for registration of new keyed histograms. Uses
    /// atomic to avoid the use of &mut.
    keys_keyed: KeyGenerator<Map>,

    /// A shared boolean that may be turned on/off to (de)activate
    /// Telemetry.
    is_active: Arc<AtomicBool>,

    /// Connection to the thread holding all the storage of this
    /// instance of the service.
    sender: Sender<Op>,
}

// Backstage pass used inside the crate.
impl PrivateAccess {
    pub fn register_plain(service: &Service, name: String, storage: Box<PlainRawStorage>) -> Key<Plain> {
        service.register_plain(name, storage)
    }

    pub fn register_keyed<T>(service: &Service, name: String, storage: Box<KeyedRawStorage>) -> Key<Keyed<T>> {
        service.register_keyed(name, storage)
    }

    pub fn get_sender(service: &Service) -> &Sender<Op> {
        &service.sender
    }

    pub fn get_is_active(service: &Service) -> &Arc<AtomicBool> {
        &service.is_active
    }
}

pub struct PrivateAccess;

<commit_msg>Doc: Service::new<commit_after>extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use std::sync::mpsc::{channel, Sender};

use misc::{NamedStorage, SerializationFormat, Subset};
use task::{Op, PlainRawStorage, KeyedRawStorage, TelemetryTask};
use indexing::*;

///
/// The Telemetry service.
///
/// The service is in charge of maintaining the data recorded by the
/// histograms. Each application using telemetry needs one instance of
/// the service (or more, but this should seldom be necessary). The
/// data is stored and processed in a dedicated background thread and
/// the memory is recollected only when the service is dropped.
///
/// # Panics
///
/// The service will panic if an attempt is made to register two
/// histograms with the same key.
///
impl Service {
    ///
    /// Create a new instance of the service.
    ///
    /// This immediately launches the thread owning the data.
    ///
    /// If `is_active` is `false`, the service will immediately accept
    /// records. Otherwise, the service will only start recording once
    /// `set_active(true)` has been called.
    ///
    pub fn new(is_active: bool) -> Service {
        let (sender, receiver) = channel();
        thread::spawn(|| {
            let mut task = TelemetryTask::new(receiver);
            task.run()
        });
        Service {
            keys_plain: KeyGenerator::new(),
            keys_keyed: KeyGenerator::new(),
            sender: sender,
            is_active: Arc::new(AtomicBool::new(is_active)),
        }
    }

    ///
    /// Serialize all histograms as json, in a given format.
    ///
    /// # Panics
    ///
    /// The service will panic if the sender is closed by the time serialization
    /// is complete.
    ///
    pub fn to_json(&self, what: Subset, format: SerializationFormat, sender: Sender<Json>) {
        self.sender.send(Op::Serialize(what, format, sender)).unwrap();
    }

    ///
    /// Make the service (in)active.
    ///
    /// Any data recorded on a histogram while the service is inactive will be ignored.
    ///
    pub fn set_active(&self, value: bool) {
        self.is_active.store(value, Ordering::Relaxed);
    }

    pub fn is_active(&self) -> bool {
        self.is_active.load(Ordering::Relaxed)
    }

    ///
    /// Register a plain histogram, returning a fresh key.
    ///
    fn register_plain(&self, name: String, storage: Box<PlainRawStorage>) -> Key<Plain> {
        let key = self.keys_plain.next();
        let named = NamedStorage { name: name, contents: storage };
        self.sender.send(Op::RegisterPlain(key.index, named)).unwrap();
        key
    }

    ///
    /// Register a keyed histogram, returning a fresh key.
    ///
    fn register_keyed<T>(&self, name: String, storage: Box<KeyedRawStorage>) -> Key<Keyed<T>> {
        let key = self.keys_keyed.next();
        let named = NamedStorage { name: name, contents: storage };
        self.sender.send(Op::RegisterKeyed(key.index, named)).unwrap();
        key
    }
}

/// Upon death of the service, terminate the thread and recollect all
/// owned memory.
impl Drop for Service {
    fn drop(&mut self) {
        let _ = self.sender.send(Op::Terminate);
    }
}

pub struct Service {
    /// A key generator for registration of new plain histograms. Uses
    /// atomic to avoid the use of &mut.
    keys_plain: KeyGenerator<Plain>,

    /// A key generator for registration of new keyed histograms. Uses
    /// atomic to avoid the use of &mut.
    keys_keyed: KeyGenerator<Map>,

    /// A shared boolean that may be turned on/off to (de)activate
    /// Telemetry.
    is_active: Arc<AtomicBool>,

    /// Connection to the thread holding all the storage of this
    /// instance of the service.
    sender: Sender<Op>,
}

// Backstage pass used inside the crate.
impl PrivateAccess {
    pub fn register_plain(service: &Service, name: String, storage: Box<PlainRawStorage>) -> Key<Plain> {
        service.register_plain(name, storage)
    }

    pub fn register_keyed<T>(service: &Service, name: String, storage: Box<KeyedRawStorage>) -> Key<Keyed<T>> {
        service.register_keyed(name, storage)
    }

    pub fn get_sender(service: &Service) -> &Sender<Op> {
        &service.sender
    }

    pub fn get_is_active(service: &Service) -> &Arc<AtomicBool> {
        &service.is_active
    }
}

pub struct PrivateAccess;

<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::process::Command;
use std::io::{self, Write};
use std::{fs, env};

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot, Target};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(
    cmode: &CompilationMode,
    blueprint: Blueprint,
    ctoml: &cargo::Toml,
    home: &Home,
    rustflags: &Rustflags,
    src: &Src,
    sysroot: &Sysroot,
    hash: u64,
    verbose: bool,
) -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib
        .remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;

    if cmode.triple().contains("pc-windows-gnu") {
        let src = &sysroot.path()
            .join("lib")
            .join("rustlib")
            .join(cmode.triple())
            .join("lib");

        // These are required for linking executables/dlls
        for file in ["rsbegin.o", "rsend.o", "crt2.o", "dllcrt2.o"].iter() {
            let file_src = src.join(file);
            let file_dst = dst.join(file);
            fs::copy(&file_src, &file_dst)
                .chain_err(|| format!("couldn't copy {} to {}", file_src.display(), file_dst.display()))?;
        }
    }

    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();

        let mut map = Table::new();
        map.insert("dependencies".to_owned(), Value::Table(stage.toml));
        stoml.push_str(&Value::Table(map).to_string());

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        {
            // Recent rust-src comes with a lockfile for libstd. Use it.
            let lockfile = src.path().join("Cargo.lock");
            if lockfile.exists() {
                fs::copy(lockfile, &td.join("Cargo.lock")).chain_err(|| "couldn't copy lock file")?;
            }
        }
        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            let mut flags = rustflags.for_xargo(home);
            flags.push_str(" -Z force-unstable-if-unmarked");
            if verbose {
                writeln!(io::stderr(), "+ RUSTFLAGS={:?}", flags).ok();
            }
            cmd.env("RUSTFLAGS", flags);
            cmd.env_remove("CARGO_TARGET_DIR");

            // As of rust-lang/cargo#4788 Cargo invokes rustc with a changed "current directory" so
            // we can't assume that such directory will be the same as the directory from which
            // Xargo was invoked. This is specially true when compiling the sysroot as the std
            // source is provided as a workspace and Cargo will change the current directory to the
            // root of the workspace when building one. To ensure rustc finds a target specification
            // file stored in the current directory we'll set `RUST_TARGET_PATH`  to the current
            // directory.
            if env::var_os("RUST_TARGET_PATH").is_none() {
                if let CompilationMode::Cross(ref target) = *cmode {
                    if let Target::Custom { ref json, .. } = *target {
                        cmd.env("RUST_TARGET_PATH", json.parent().unwrap());
                    }
                }
            }

            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(
            &td.join("target")
                .join(cmode.triple())
                .join(profile())
                .join("deps"),
            &dst,
        )?;
    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(
    cmode: &CompilationMode,
    blueprint: &Blueprint,
    rustflags: &Rustflags,
    ctoml: &cargo::Toml,
    meta: &VersionMeta,
) -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(
    cmode: &CompilationMode,
    home: &Home,
    root: &Root,
    rustflags: &Rustflags,
    meta: &VersionMeta,
    src: &Src,
    sysroot: &Sysroot,
    verbose: bool,
) -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), root, &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(
            cmode,
            blueprint,
            &ctoml,
            home,
            rustflags,
            src,
            sysroot,
            hash,
            verbose,
        )?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(
        &sysroot
            .path()
            .join("lib/rustlib")
            .join(&meta.host)
            .join("lib"),
        &dst,
    )?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    toml: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint {
            stages: BTreeMap::new(),
        }
    }

    fn from(toml: Option<&xargo::Toml>, target: &str, root: &Root, src: &Src) -> Result<Self> {
        let deps = match (
            toml.and_then(|t| t.dependencies()),
            toml.and_then(|t| t.target_dependencies(target)),
        ) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value
                    .as_table()
                    .cloned()
                    .ok_or_else(|| format!("Xargo.toml: `dependencies` must be a table"))?;

                let more_deps = tvalue.as_table().ok_or_else(|| {
                    format!(
                        "Xargo.toml: `target.{}.dependencies` must be \
                         a table",
                        target
                    )
                })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!(
                            "found duplicate dependency name {}, \
                             but all dependencies must have a \
                             unique name",
                            k
                        ))?
                    }
                }

                deps
            }
            (Some(value), None) | (None, Some(value)) => if let Some(table) = value.as_table() {
                table.clone()
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies must be \
                     a table",
                    target
                ))?
            },
            (None, None) => {
                // If no dependencies were listed, we assume `core` as the
                // only dependency
                let mut t = BTreeMap::new();
                t.insert("core".to_owned(), Value::Table(BTreeMap::new()));
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value
                        .as_integer()
                        .ok_or_else(|| format!("dependencies.{}.stage must be an integer", k))?
                } else {
                    0
                };

                if let Some(path) = map.get_mut("path") {
                    let p = PathBuf::from(path.as_str()
                        .ok_or_else(|| format!("dependencies.{}.path must be a string", k))?);

                    if !p.is_absolute() {
                        *path = Value::String(
                            root.path()
                                .join(&p)
                                .canonicalize()
                                .chain_err(|| format!("couldn't canonicalize {}", p.display()))?
                                .display()
                                .to_string(),
                        );
                    }
                }

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path().join(format!("lib{}", k)).display().to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map);
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies.{} must be \
                     a table",
                    target,
                    k
                ))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table) {
        let stage = self.stages.entry(stage).or_insert_with(|| {
            Stage {
                crates: vec![],
                toml: Table::new(),
            }
        });

        stage.toml.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
    where
        H: Hasher,
    {
        for stage in self.stages.values() {
            for (k, v) in stage.toml.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<commit_msg>Revert "use Cargo.lock file from rust-src if present"<commit_after>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::path::PathBuf;
use std::process::Command;
use std::io::{self, Write};
use std::{env, fs};

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot, Target};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(
    cmode: &CompilationMode,
    blueprint: Blueprint,
    ctoml: &cargo::Toml,
    home: &Home,
    rustflags: &Rustflags,
    sysroot: &Sysroot,
    hash: u64,
    verbose: bool,
) -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib
        .remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;

    if cmode.triple().contains("pc-windows-gnu") {
        let src = &sysroot.path()
            .join("lib")
            .join("rustlib")
            .join(cmode.triple())
            .join("lib");

        // These are required for linking executables/dlls
        for file in ["rsbegin.o", "rsend.o", "crt2.o", "dllcrt2.o"].iter() {
            let file_src = src.join(file);
            let file_dst = dst.join(file);
            fs::copy(&file_src, &file_dst)
                .chain_err(|| format!("couldn't copy {} to {}", file_src.display(), file_dst.display()))?;
        }
    }

    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();

        let mut map = Table::new();
        map.insert("dependencies".to_owned(), Value::Table(stage.toml));
        stoml.push_str(&Value::Table(map).to_string());

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            let mut flags = rustflags.for_xargo(home);
            flags.push_str(" -Z force-unstable-if-unmarked");
            if verbose {
                writeln!(io::stderr(), "+ RUSTFLAGS={:?}", flags).ok();
            }
            cmd.env("RUSTFLAGS", flags);
            cmd.env_remove("CARGO_TARGET_DIR");

            // As of rust-lang/cargo#4788 Cargo invokes rustc with a changed "current directory" so
            // we can't assume that such directory will be the same as the directory from which
            // Xargo was invoked. This is specially true when compiling the sysroot as the std
            // source is provided as a workspace and Cargo will change the current directory to the
            // root of the workspace when building one. To ensure rustc finds a target specification
            // file stored in the current directory we'll set `RUST_TARGET_PATH`  to the current
            // directory.
            if env::var_os("RUST_TARGET_PATH").is_none() {
                if let CompilationMode::Cross(ref target) = *cmode {
                    if let Target::Custom { ref json, .. } = *target {
                        cmd.env("RUST_TARGET_PATH", json.parent().unwrap());
                    }
                }
            }

            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(
            &td.join("target")
                .join(cmode.triple())
                .join(profile())
                .join("deps"),
            &dst,
        )?;
    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(
    cmode: &CompilationMode,
    blueprint: &Blueprint,
    rustflags: &Rustflags,
    ctoml: &cargo::Toml,
    meta: &VersionMeta,
) -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(
    cmode: &CompilationMode,
    home: &Home,
    root: &Root,
    rustflags: &Rustflags,
    meta: &VersionMeta,
    src: &Src,
    sysroot: &Sysroot,
    verbose: bool,
) -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), root, &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(cmode, blueprint, &ctoml, home, rustflags, sysroot, hash, verbose)?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(
        &sysroot
            .path()
            .join("lib/rustlib")
            .join(&meta.host)
            .join("lib"),
        &dst,
    )?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    toml: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint {
            stages: BTreeMap::new(),
        }
    }

    fn from(toml: Option<&xargo::Toml>, target: &str, root: &Root, src: &Src) -> Result<Self> {
        let deps = match (
            toml.and_then(|t| t.dependencies()),
            toml.and_then(|t| t.target_dependencies(target)),
        ) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value
                    .as_table()
                    .cloned()
                    .ok_or_else(|| format!("Xargo.toml: `dependencies` must be a table"))?;

                let more_deps = tvalue.as_table().ok_or_else(|| {
                    format!(
                        "Xargo.toml: `target.{}.dependencies` must be \
                         a table",
                        target
                    )
                })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!(
                            "found duplicate dependency name {}, \
                             but all dependencies must have a \
                             unique name",
                            k
                        ))?
                    }
                }

                deps
            }
            (Some(value), None) | (None, Some(value)) => if let Some(table) = value.as_table() {
                table.clone()
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies must be \
                     a table",
                    target
                ))?
            },
            (None, None) => {
                // If no dependencies were listed, we assume `core` as the
                // only dependency
                let mut t = BTreeMap::new();
                t.insert("core".to_owned(), Value::Table(BTreeMap::new()));
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value
                        .as_integer()
                        .ok_or_else(|| format!("dependencies.{}.stage must be an integer", k))?
                } else {
                    0
                };

                if let Some(path) = map.get_mut("path") {
                    let p = PathBuf::from(path.as_str()
                        .ok_or_else(|| format!("dependencies.{}.path must be a string", k))?);

                    if !p.is_absolute() {
                        *path = Value::String(
                            root.path()
                                .join(&p)
                                .canonicalize()
                                .chain_err(|| format!("couldn't canonicalize {}", p.display()))?
                                .display()
                                .to_string(),
                        );
                    }
                }

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path().join(format!("lib{}", k)).display().to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map);
            } else {
                Err(format!(
                    "Xargo.toml: target.{}.dependencies.{} must be \
                     a table",
                    target,
                    k
                ))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table) {
        let stage = self.stages.entry(stage).or_insert_with(|| {
            Stage {
                crates: vec![],
                toml: Table::new(),
            }
        });

        stage.toml.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
    where
        H: Hasher,
    {
        for stage in self.stages.values() {
            for (k, v) in stage.toml.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<|endoftext|>


<commit_before>pub use luminance::pixel::{Depth32F, R32F, RGBA32F};
pub use luminance::texture::{Dim2, Flat, Sampler, Texture};
use image;
use std::ops::Deref;
use std::path::Path;

use resource::{Load, LoadError, Reload, ResCache, Result};

// Common texture aliases.
pub type TextureRGBA32F = Texture<Flat, Dim2, RGBA32F>;
pub type TextureDepth32F = Texture<Flat, Dim2, Depth32F>;

/// Load an RGBA texture from an image at a path.
///
/// The `linearizer` argument is an option that gives the factor to apply to linearize if needed. Pass
/// `None` if the texture is already linearized.
pub fn load_rgba_texture<P, L>(path: P, sampler: &Sampler, linearizer: L) -> Result<TextureRGBA32F> where P: AsRef<Path>, L: Into<Option<f32>> {
  info!("loading texture image: \x1b[35m{:?}", path.as_ref());

  let img = image::open(path).map_err(|e| LoadError::ConversionFailed(format!("{:?}", e)))?.flipv().to_rgba();
  let dim = img.dimensions();
  let linearizer = linearizer.into();
  let raw: Vec<f32> = img.into_raw().into_iter().map(|x| {
    let y = x as f32 / 255.;
    linearizer.map_or(y, |factor| y.powf(1. / factor))
  }).collect();

  let tex = Texture::new(dim, 0, sampler).map_err(|e| LoadError::ConversionFailed(format!("{:?}", e)))?;
  tex.upload_raw(false, &raw);

  Ok(tex)
}

/// Save an RGBA image on disk.
pub fn save_rgba_texture<P>(texture: &TextureRGBA32F, path: P) where P: AsRef<Path> {
  info!("saving texture image to: \x1b[35m{:?}", path.as_ref());

  let texels = texture.get_raw_texels();
  let (w, h) = texture.size();
  let mut output = Vec::with_capacity((w * h) as usize);

  for texel in &texels {
    output.push((texel * 255.) as u8);
  }

  let _ = image::save_buffer(path, &output, w, h, image::ColorType::RGBA(8));
}

pub struct TextureImage {
  pub texture: TextureRGBA32F,
  sampler: Sampler,
  linearizer: Option<f32>,
}

impl Deref for TextureImage {
  type Target = TextureRGBA32F;

  fn deref(&self) -> &Self::Target {
    &self.texture
  }
}

impl Load for TextureImage {
  type Args = (Sampler, Option<f32>);

  const TY_STR: &'static str = "textures";

  fn load<P>(path: P, _: &mut ResCache, (sampler, linearizer): Self::Args) -> Result<Self> where P: AsRef<Path> {
    load_rgba_texture(path, &sampler, linearizer)
      .map(|tex| TextureImage {
        texture: tex,
        sampler: sampler,
        linearizer: linearizer
      })
  }
}

impl Reload for TextureImage {
  fn reload_args(&self) -> Self::Args {
    (self.sampler, self.linearizer)
  }
}
<commit_msg>Exported MagFilter, MinFilter and Wrap from texture.rs.<commit_after>pub use luminance::pixel::{Depth32F, R32F, RGBA32F};
pub use luminance::texture::{Dim2, Flat, MagFilter, MinFilter, Sampler, Texture, Wrap};
use image;
use std::ops::Deref;
use std::path::Path;

use resource::{Load, LoadError, Reload, ResCache, Result};

// Common texture aliases.
pub type TextureRGBA32F = Texture<Flat, Dim2, RGBA32F>;
pub type TextureDepth32F = Texture<Flat, Dim2, Depth32F>;

/// Load an RGBA texture from an image at a path.
///
/// The `linearizer` argument is an option that gives the factor to apply to linearize if needed. Pass
/// `None` if the texture is already linearized.
pub fn load_rgba_texture<P, L>(path: P, sampler: &Sampler, linearizer: L) -> Result<TextureRGBA32F> where P: AsRef<Path>, L: Into<Option<f32>> {
  info!("loading texture image: \x1b[35m{:?}", path.as_ref());

  let img = image::open(path).map_err(|e| LoadError::ConversionFailed(format!("{:?}", e)))?.flipv().to_rgba();
  let dim = img.dimensions();
  let linearizer = linearizer.into();
  let raw: Vec<f32> = img.into_raw().into_iter().map(|x| {
    let y = x as f32 / 255.;
    linearizer.map_or(y, |factor| y.powf(1. / factor))
  }).collect();

  let tex = Texture::new(dim, 0, sampler).map_err(|e| LoadError::ConversionFailed(format!("{:?}", e)))?;
  tex.upload_raw(false, &raw);

  Ok(tex)
}

/// Save an RGBA image on disk.
pub fn save_rgba_texture<P>(texture: &TextureRGBA32F, path: P) where P: AsRef<Path> {
  info!("saving texture image to: \x1b[35m{:?}", path.as_ref());

  let texels = texture.get_raw_texels();
  let (w, h) = texture.size();
  let mut output = Vec::with_capacity((w * h) as usize);

  for texel in &texels {
    output.push((texel * 255.) as u8);
  }

  let _ = image::save_buffer(path, &output, w, h, image::ColorType::RGBA(8));
}

pub struct TextureImage {
  pub texture: TextureRGBA32F,
  sampler: Sampler,
  linearizer: Option<f32>,
}

impl Deref for TextureImage {
  type Target = TextureRGBA32F;

  fn deref(&self) -> &Self::Target {
    &self.texture
  }
}

impl Load for TextureImage {
  type Args = (Sampler, Option<f32>);

  const TY_STR: &'static str = "textures";

  fn load<P>(path: P, _: &mut ResCache, (sampler, linearizer): Self::Args) -> Result<Self> where P: AsRef<Path> {
    load_rgba_texture(path, &sampler, linearizer)
      .map(|tex| TextureImage {
        texture: tex,
        sampler: sampler,
        linearizer: linearizer
      })
  }
}

impl Reload for TextureImage {
  fn reload_args(&self) -> Self::Args {
    (self.sampler, self.linearizer)
  }
}
<|endoftext|>


<commit_before>//! Rendering functionality and abstractions
//!
//! Collection of common traits and implementations
//! to facilitate (possible hardware-accelerated) rendering.
//!
//! Supported rendering apis:
//!
//! - Raw OpenGL ES 2

use std::collections::HashSet;
use std::error::Error;

#[cfg(feature = "wayland_frontend")]
use crate::{utils::Rectangle, wayland::compositor::SurfaceAttributes};
use cgmath::{prelude::*, Matrix3, Vector2};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};

#[cfg(feature = "renderer_gl")]
pub mod gles2;
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
use crate::backend::egl::display::EGLBufferReader;

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
pub enum Transform {
    /// Identity transformation (plane is unaltered when applied)
    Normal,
    /// Plane is rotated by 90 degrees
    _90,
    /// Plane is rotated by 180 degrees
    _180,
    /// Plane is rotated by 270 degrees
    _270,
    /// Plane is flipped vertically
    Flipped,
    /// Plane is flipped vertically and rotated by 90 degrees
    Flipped90,
    /// Plane is flipped vertically and rotated by 180 degrees
    Flipped180,
    /// Plane is flipped vertically and rotated by 270 degrees
    Flipped270,
}

impl Transform {
    /// A projection matrix to apply this transformation
    pub fn matrix(&self) -> Matrix3<f32> {
        match self {
            Transform::Normal => Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_90 => Matrix3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
        }
    }

    /// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
    ///
    /// Flipping is preserved and 180/Normal transformation are uneffected.
    pub fn invert(&self) -> Transform {
        match self {
            Transform::Normal => Transform::Normal,
            Transform::Flipped => Transform::Flipped,
            Transform::_90 => Transform::_270,
            Transform::_180 => Transform::_180,
            Transform::_270 => Transform::_90,
            Transform::Flipped90 => Transform::Flipped270,
            Transform::Flipped180 => Transform::Flipped180,
            Transform::Flipped270 => Transform::Flipped90,
        }
    }

    /// Transformed size after applying this transformation.
    pub fn transform_size(&self, width: u32, height: u32) -> (u32, u32) {
        if *self == Transform::_90
            || *self == Transform::_270
            || *self == Transform::Flipped90
            || *self == Transform::Flipped270
        {
            (height, width)
        } else {
            (width, height)
        }
    }
}

#[cfg(feature = "wayland-frontend")]
impl From<wayland_server::protocol::wl_output::Transform> for Transform {
    fn from(transform: wayland_server::protocol::wl_output::Transform) -> Transform {
        use wayland_server::protocol::wl_output::Transform::*;
        match transform {
            Normal => Transform::Normal,
            _90 => Transform::_90,
            _180 => Transform::_180,
            _270 => Transform::_270,
            Flipped => Transform::Flipped,
            Flipped90 => Transform::Flipped90,
            Flipped180 => Transform::Flipped180,
            Flipped270 => Transform::Flipped270,
        }
    }
}

/// Abstraction for Renderers, that can render into different targets
pub trait Bind<Target>: Unbind {
    /// Bind a given rendering target, which will contain the rendering results until `unbind` is called.
    ///
    /// Binding to target, while another one is already bound, is rendering defined.
    /// Some renderers might happily replace the current target, while other might drop the call
    /// or throw an error.
    fn bind(&mut self, target: Target) -> Result<(), <Self as Renderer>::Error>;
    /// Supported pixel formats for given targets, if applicable.
    fn supported_formats(&self) -> Option<HashSet<crate::backend::allocator::Format>> {
        None
    }
}

/// Functionality to unbind the current rendering target
pub trait Unbind: Renderer {
    /// Unbind the current rendering target.
    ///
    /// May fall back to a default target, if defined by the implementation.
    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error>;
}

/// A two dimensional texture
pub trait Texture {
    /// Size of the texture plane (w x h)
    fn size(&self) -> (u32, u32) {
        (self.width(), self.height())
    }

    /// Width of the texture plane
    fn width(&self) -> u32;
    /// Height of the texture plane
    fn height(&self) -> u32;
}

/// Helper trait for [`Renderer`], which defines a rendering api for a currently in-progress frame during [`Renderer::render`].
pub trait Frame {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;

    /// Clear the complete current target with a single given color.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
    /// Render a texture to the current target using given projection matrix and alpha.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture(
        &mut self,
        texture: &Self::TextureId,
        matrix: Matrix3<f32>,
        alpha: f32,
    ) -> Result<(), Self::Error>;
    /// Render a texture to the current target as a flat 2d-plane at a given
    /// position, applying the given transformation with the given alpha value.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture_at(
        &mut self,
        texture: &Self::TextureId,
        pos: (i32, i32),
        transform: Transform,
        alpha: f32,
    ) -> Result<(), Self::Error> {
        let mut mat = Matrix3::<f32>::identity();

        // position and scale
        let size = texture.size();
        mat = mat * Matrix3::from_translation(Vector2::new(pos.0 as f32, pos.1 as f32));
        mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32);

        //apply surface transformation
        mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
        if transform == Transform::Normal {
            assert_eq!(mat, mat * transform.invert().matrix());
            assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
        }
        mat = mat * transform.invert().matrix();
        mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));

        self.render_texture(texture, mat, alpha)
    }
}

/// Abstraction of commonly used rendering operations for compositors.
pub trait Renderer {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;
    /// Type representing a currently in-progress frame during the [`Renderer::render`]-call
    type Frame: Frame<Error = Self::Error, TextureId = Self::TextureId>;

    /// Import a given bitmap into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it,
    /// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
    ///
    /// This operation needs no bound or default rendering target.
    #[cfg(feature = "image")]
    fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
        &mut self,
        image: &image::ImageBuffer<image::Rgba<u8>, C>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Returns supported formats for shared memory buffers.
    ///
    /// Will always contain At least `Argb8888` and `Xrgb8888`.
    #[cfg(feature = "wayland_frontend")]
    fn shm_formats(&self) -> &[wl_shm::Format] {
        // Mandatory
        &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888]
    }

    /// Import a given buffer into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
    ///
    /// This operation needs no bound or default rendering target.
    ///
    /// The implementation defines, if the id keeps being valid, if the buffer is released,
    /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
    ///
    /// If provided the `SurfaceAttributes` can be used to do caching of rendering resources and is generally recommended.
    ///
    /// The `damage` argument provides a list of rectangle locating parts of the buffer that need to be updated. When provided
    /// with an empty list `&[]`, the renderer is allowed to not update the texture at all.
    #[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
    fn import_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        surface: Option<&SurfaceAttributes>,
        damage: &[Rectangle],
        egl: Option<&EGLBufferReader>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Initialize a rendering context on the current rendering target with given dimensions and transformation.
    ///
    /// This function *may* error, if:
    /// - The given dimensions are unsuppored (too large) for this renderer
    /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
    /// - There was a previous `begin`-call, which was not terminated by `finish`.
    /// - This renderer implements `Bind`, no target was bound *and* has no default target.
    /// - (Renderers not implementing `Bind` always have a default target.)
    fn render<F, R>(
        &mut self,
        width: u32,
        height: u32,
        transform: Transform,
        rendering: F,
    ) -> Result<R, Self::Error>
    where
        F: FnOnce(&mut Self, &mut Self::Frame) -> R;
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
pub fn buffer_dimensions(
    buffer: &wl_buffer::WlBuffer,
    egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<(i32, i32)> {
    if let Some((w, h)) = egl_buffer_reader
        .as_ref()
        .and_then(|x| x.egl_buffer_dimensions(&buffer))
    {
        Some((w, h))
    } else if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}

/// Returns the dimensions of a wl_buffer
#[cfg(all(feature = "wayland_frontend", not(feature = "backend_egl")))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> {
    use crate::backend::allocator::Buffer;

    if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}
<commit_msg>renderer: Add a way to query the buffer *type*<commit_after>//! Rendering functionality and abstractions
//!
//! Collection of common traits and implementations
//! to facilitate (possible hardware-accelerated) rendering.
//!
//! Supported rendering apis:
//!
//! - Raw OpenGL ES 2

use std::collections::HashSet;
use std::error::Error;

#[cfg(feature = "wayland_frontend")]
use crate::{utils::Rectangle, wayland::compositor::SurfaceAttributes};
use cgmath::{prelude::*, Matrix3, Vector2};
#[cfg(feature = "wayland_frontend")]
use wayland_server::protocol::{wl_buffer, wl_shm};

#[cfg(feature = "renderer_gl")]
pub mod gles2;
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
use crate::backend::egl::display::EGLBufferReader;

#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes
pub enum Transform {
    /// Identity transformation (plane is unaltered when applied)
    Normal,
    /// Plane is rotated by 90 degrees
    _90,
    /// Plane is rotated by 180 degrees
    _180,
    /// Plane is rotated by 270 degrees
    _270,
    /// Plane is flipped vertically
    Flipped,
    /// Plane is flipped vertically and rotated by 90 degrees
    Flipped90,
    /// Plane is flipped vertically and rotated by 180 degrees
    Flipped180,
    /// Plane is flipped vertically and rotated by 270 degrees
    Flipped270,
}

impl Transform {
    /// A projection matrix to apply this transformation
    pub fn matrix(&self) -> Matrix3<f32> {
        match self {
            Transform::Normal => Matrix3::new(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_90 => Matrix3::new(0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_180 => Matrix3::new(-1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::_270 => Matrix3::new(0.0, 1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped => Matrix3::new(-1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped90 => Matrix3::new(0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped180 => Matrix3::new(1.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 1.0),
            Transform::Flipped270 => Matrix3::new(0.0, -1.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0),
        }
    }

    /// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
    ///
    /// Flipping is preserved and 180/Normal transformation are uneffected.
    pub fn invert(&self) -> Transform {
        match self {
            Transform::Normal => Transform::Normal,
            Transform::Flipped => Transform::Flipped,
            Transform::_90 => Transform::_270,
            Transform::_180 => Transform::_180,
            Transform::_270 => Transform::_90,
            Transform::Flipped90 => Transform::Flipped270,
            Transform::Flipped180 => Transform::Flipped180,
            Transform::Flipped270 => Transform::Flipped90,
        }
    }

    /// Transformed size after applying this transformation.
    pub fn transform_size(&self, width: u32, height: u32) -> (u32, u32) {
        if *self == Transform::_90
            || *self == Transform::_270
            || *self == Transform::Flipped90
            || *self == Transform::Flipped270
        {
            (height, width)
        } else {
            (width, height)
        }
    }
}

#[cfg(feature = "wayland-frontend")]
impl From<wayland_server::protocol::wl_output::Transform> for Transform {
    fn from(transform: wayland_server::protocol::wl_output::Transform) -> Transform {
        use wayland_server::protocol::wl_output::Transform::*;
        match transform {
            Normal => Transform::Normal,
            _90 => Transform::_90,
            _180 => Transform::_180,
            _270 => Transform::_270,
            Flipped => Transform::Flipped,
            Flipped90 => Transform::Flipped90,
            Flipped180 => Transform::Flipped180,
            Flipped270 => Transform::Flipped270,
        }
    }
}

/// Abstraction for Renderers, that can render into different targets
pub trait Bind<Target>: Unbind {
    /// Bind a given rendering target, which will contain the rendering results until `unbind` is called.
    ///
    /// Binding to target, while another one is already bound, is rendering defined.
    /// Some renderers might happily replace the current target, while other might drop the call
    /// or throw an error.
    fn bind(&mut self, target: Target) -> Result<(), <Self as Renderer>::Error>;
    /// Supported pixel formats for given targets, if applicable.
    fn supported_formats(&self) -> Option<HashSet<crate::backend::allocator::Format>> {
        None
    }
}

/// Functionality to unbind the current rendering target
pub trait Unbind: Renderer {
    /// Unbind the current rendering target.
    ///
    /// May fall back to a default target, if defined by the implementation.
    fn unbind(&mut self) -> Result<(), <Self as Renderer>::Error>;
}

/// A two dimensional texture
pub trait Texture {
    /// Size of the texture plane (w x h)
    fn size(&self) -> (u32, u32) {
        (self.width(), self.height())
    }

    /// Width of the texture plane
    fn width(&self) -> u32;
    /// Height of the texture plane
    fn height(&self) -> u32;
}

/// Helper trait for [`Renderer`], which defines a rendering api for a currently in-progress frame during [`Renderer::render`].
pub trait Frame {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;

    /// Clear the complete current target with a single given color.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn clear(&mut self, color: [f32; 4]) -> Result<(), Self::Error>;
    /// Render a texture to the current target using given projection matrix and alpha.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture(
        &mut self,
        texture: &Self::TextureId,
        matrix: Matrix3<f32>,
        alpha: f32,
    ) -> Result<(), Self::Error>;
    /// Render a texture to the current target as a flat 2d-plane at a given
    /// position, applying the given transformation with the given alpha value.
    ///
    /// This operation is only valid in between a `begin` and `finish`-call.
    /// If called outside this operation may error-out, do nothing or modify future rendering results in any way.
    fn render_texture_at(
        &mut self,
        texture: &Self::TextureId,
        pos: (i32, i32),
        transform: Transform,
        alpha: f32,
    ) -> Result<(), Self::Error> {
        let mut mat = Matrix3::<f32>::identity();

        // position and scale
        let size = texture.size();
        mat = mat * Matrix3::from_translation(Vector2::new(pos.0 as f32, pos.1 as f32));
        mat = mat * Matrix3::from_nonuniform_scale(size.0 as f32, size.1 as f32);

        //apply surface transformation
        mat = mat * Matrix3::from_translation(Vector2::new(0.5, 0.5));
        if transform == Transform::Normal {
            assert_eq!(mat, mat * transform.invert().matrix());
            assert_eq!(transform.matrix(), Matrix3::<f32>::identity());
        }
        mat = mat * transform.invert().matrix();
        mat = mat * Matrix3::from_translation(Vector2::new(-0.5, -0.5));

        self.render_texture(texture, mat, alpha)
    }
}

/// Abstraction of commonly used rendering operations for compositors.
pub trait Renderer {
    /// Error type returned by the rendering operations of this renderer.
    type Error: Error;
    /// Texture Handle type used by this renderer.
    type TextureId: Texture;
    /// Type representing a currently in-progress frame during the [`Renderer::render`]-call
    type Frame: Frame<Error = Self::Error, TextureId = Self::TextureId>;

    /// Import a given bitmap into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it,
    /// and needs to be freed by calling `destroy_texture` on this renderer to avoid a resource leak.
    ///
    /// This operation needs no bound or default rendering target.
    #[cfg(feature = "image")]
    fn import_bitmap<C: std::ops::Deref<Target = [u8]>>(
        &mut self,
        image: &image::ImageBuffer<image::Rgba<u8>, C>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Returns supported formats for shared memory buffers.
    ///
    /// Will always contain At least `Argb8888` and `Xrgb8888`.
    #[cfg(feature = "wayland_frontend")]
    fn shm_formats(&self) -> &[wl_shm::Format] {
        // Mandatory
        &[wl_shm::Format::Argb8888, wl_shm::Format::Xrgb8888]
    }

    /// Import a given buffer into the renderer.
    ///
    /// Returns a texture_id, which can be used with `render_texture(_at)` or implementation-specific functions.
    ///
    /// If not otherwise defined by the implementation, this texture id is only valid for the renderer, that created it.
    ///
    /// This operation needs no bound or default rendering target.
    ///
    /// The implementation defines, if the id keeps being valid, if the buffer is released,
    /// to avoid relying on implementation details, keep the buffer alive, until you destroyed this texture again.
    ///
    /// If provided the `SurfaceAttributes` can be used to do caching of rendering resources and is generally recommended.
    ///
    /// The `damage` argument provides a list of rectangle locating parts of the buffer that need to be updated. When provided
    /// with an empty list `&[]`, the renderer is allowed to not update the texture at all.
    #[cfg(all(feature = "wayland_frontend", feature = "backend_egl"))]
    fn import_buffer(
        &mut self,
        buffer: &wl_buffer::WlBuffer,
        surface: Option<&SurfaceAttributes>,
        damage: &[Rectangle],
        egl: Option<&EGLBufferReader>,
    ) -> Result<Self::TextureId, Self::Error>;

    /// Initialize a rendering context on the current rendering target with given dimensions and transformation.
    ///
    /// This function *may* error, if:
    /// - The given dimensions are unsuppored (too large) for this renderer
    /// - The given Transformation is not supported by the renderer (`Transform::Normal` is always supported).
    /// - This renderer implements `Bind`, no target was bound *and* has no default target.
    /// - (Renderers not implementing `Bind` always have a default target.)
    fn render<F, R>(
        &mut self,
        width: u32,
        height: u32,
        transform: Transform,
        rendering: F,
    ) -> Result<R, Self::Error>
    where
        F: FnOnce(&mut Self, &mut Self::Frame) -> R;
}

#[cfg(feature = "wayland_frontend")]
#[non_exhaustive]
/// Buffer type of a given wl_buffer, if managed by smithay
pub enum BufferType {
    /// Buffer is managed by the [`crate::wayland::shm`] global
    Shm,
    #[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
    /// Buffer is managed by a currently initialized [`crate::backend::egl::display::EGLBufferReader`]
    Egl,
    /// Buffer is managed by the [`crate::wayland::dmabuf`] global
    Dma,
}

/// Returns the *type* of a wl_buffer
///
/// Returns `None` if the type is not known to smithay
/// or otherwise not supported (e.g. not initialized using one of smithays [`crate::wayland`]-handlers).
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl", feature = "use_system_lib"))]
pub fn buffer_type(
    buffer: &wl_buffer::WlBuffer,
    egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<BufferType> {
    if egl_buffer_reader
        .as_ref()
        .and_then(|x| x.egl_buffer_dimensions(&buffer))
        .is_some()
    {
        Some(BufferType::Egl)
    } else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok()
    {
        Some(BufferType::Shm)
    } else {
        None
    }
}

/// Returns the *type* of a wl_buffer
///
/// Returns `None` if the type is not recognized by smithay or otherwise not supported.
#[cfg(all(feature = "wayland_frontend", not(feature = "backend_egl"), not(feature = "use_system_lib")))]
pub fn buffer_type(buffer: &wl_buffer::WlBuffer) -> Option<BufferType> {
    use crate::backend::allocator::Buffer;

    if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok()
    {
        Some(BufferType::Shm)
    } else {
        None
    }
}

/// Returns the dimensions of a wl_buffer
///
/// *Note*: This will only return dimensions for buffer types known to smithay (see [`buffer_type`])
#[cfg(all(feature = "wayland_frontend", feature = "backend_egl", feature = "use_system_lib"))]
pub fn buffer_dimensions(
    buffer: &wl_buffer::WlBuffer,
    egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<(i32, i32)> {
    if let Some((w, h)) = egl_buffer_reader
        .as_ref()
        .and_then(|x| x.egl_buffer_dimensions(&buffer))
    {
        Some((w, h))
    } else if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}

/// Returns the dimensions of a wl_buffer
///
/// *Note*: This will only return dimensions for buffer types known to smithay (see [`buffer_type`])
#[cfg(all(feature = "wayland_frontend", not(feature = "backend_egl"), not(feature = "use_system_lib")))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> {
    use crate::backend::allocator::Buffer;

    if let Ok((w, h)) =
        crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
    {
        Some((w, h))
    } else {
        None
    }
}<|endoftext|>