<commit_before>//! Iron's HTTP Request representation and associated methods.
use std::fmt::{self, Debug};
use std::net::SocketAddr;

use futures::Stream;

use http;
use http::version::Version as HttpVersion;

use method::Method;
use plugin::Extensible;
use typemap::{Key, TypeMap};

pub use hyper::Body;
pub use hyper::Request as HttpRequest;

#[cfg(test)]
use std::net::ToSocketAddrs;

pub use self::url::Url;

use error::HttpError;
use headers::{self, HeaderMap};
use {Plugin, Protocol, Set};

mod url;

/// The `Request` given to all `Middleware`.
///
/// Stores all the properties of the client's request plus
/// an `TypeMap` for data communication between middleware.
pub struct Request {
    /// The requested URL.
    pub url: Url,

    /// The local address of the request.
    pub local_addr: Option<SocketAddr>,

    /// The request headers.
    pub headers: HeaderMap,

    /// The request body as a reader.
    pub body: Option<Body>,

    /// The request method.
    pub method: Method,

    /// Extensible storage for data passed between middleware.
    pub extensions: TypeMap,

    /// The version of the HTTP protocol used.
    pub version: HttpVersion,

    _p: (),
}

impl Debug for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(writeln!(f, "Request {{"));

        try!(writeln!(f, "    url: {:?}", self.url));
        try!(writeln!(f, "    method: {:?}", self.method));
        try!(writeln!(f, "    local_addr: {:?}", self.local_addr));

        try!(write!(f, "}}"));
        Ok(())
    }
}

impl Request {
    /// Create a request from an HttpRequest.
    ///
    /// This constructor consumes the HttpRequest.
    pub fn from_http(
        req: HttpRequest<Body>,
        local_addr: Option<SocketAddr>,
        protocol: &Protocol,
    ) -> Result<Request, String> {
        let (
            http::request::Parts {
                method,
                uri,
                version,
                headers,
                ..
            },
            body,
        ) = req.into_parts();

        let url = {
            let path = uri.path();

            let mut socket_ip = String::new();
            let (host, port) = if let Some(host) = uri.host() {
                (host, uri.port_part().map(|p| p.as_u16()))
            } else if let Some(host) = headers.get(headers::HOST).and_then(|h| h.to_str().ok()) {
                let mut parts = host.split(':');
                let hostname = parts.next().unwrap();
                let port = parts.next().and_then(|p| p.parse::<u16>().ok());
                (hostname, port)
            } else if version < HttpVersion::HTTP_11 {
                if let Some(local_addr) = local_addr {
                    match local_addr {
                        SocketAddr::V4(addr4) => socket_ip.push_str(&format!("{}", addr4.ip())),
                        SocketAddr::V6(addr6) => socket_ip.push_str(&format!("[{}]", addr6.ip())),
                    }
                    (socket_ip.as_ref(), Some(local_addr.port()))
                } else {
                    return Err("No fallback host specified".into());
                }
            } else {
                return Err("No host specified in request".into());
            };

            let url_string = if let Some(port) = port {
                format!("{}://{}:{}{}", protocol.name(), host, port, path)
            } else {
                format!("{}://{}{}", protocol.name(), host, path)
            };

            match Url::parse(&url_string) {
                Ok(url) => url,
                Err(e) => return Err(format!("Couldn't parse requested URL: {}", e)),
            }
        };

        Ok(Request {
            url,
            local_addr,
            headers,
            body: Some(body),
            method,
            extensions: TypeMap::new(),
            version,
            _p: (),
        })
    }

    /// Get the contents of the body as a Vec<u8>
    ///
    /// This consumes the body future and turns it into Vec<u8>.  Note this should not be called
    /// from the main hyper thread, as it will potentially deadlock.
    pub fn get_body_contents(&mut self) -> Result<&Vec<u8>, HttpError> {
        if let Some(reader) = self.body.take() {
            let body = reader.wait().fold(Ok(Vec::new()), |r, input| {
                if let Ok(mut v) = r {
                    input.map(move |next_body_chunk| {
                        v.extend_from_slice(&next_body_chunk);
                        v
                    })
                } else {
                    r
                }
            });
            match body {
                Ok(body) => self.extensions.insert::<RequestBodyKey>(body),
                Err(e) => return Err(e),
            };
        }
        Ok(self.extensions.get::<RequestBodyKey>().unwrap())
    }

    #[cfg(test)]
    pub fn stub() -> Request {
        Request {
            url: Url::parse("http://www.rust-lang.org").unwrap(),
            local_addr: "localhost:3000".to_socket_addrs().unwrap().next(),
            headers: HeaderMap::new(),
            body: Some(Body::empty()),
            method: Method::GET,
            extensions: TypeMap::new(),
            version: HttpVersion::HTTP_11,
            _p: (),
        }
    }
}

struct RequestBodyKey;

impl Key for RequestBodyKey {
    type Value = Vec<u8>;
}

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

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

impl Plugin for Request {}
impl Set for Request {}

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

    use headers;

    use url_ext::Host::*;

    #[test]
    fn test_request_parse_absolute_uri() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "http://my-host/path".parse().unwrap();

        let iron_request = Request::from_http(hyper_request, None, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Domain("my-host"));
    }

    #[test]
    fn test_request_parse_host_header_only() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "/path".parse().unwrap();
        hyper_request
            .headers_mut()
            .insert(headers::HOST, "my-host".parse().unwrap());

        let iron_request = Request::from_http(hyper_request, None, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Domain("my-host"));
    }

    #[test]
    fn test_request_parse_host_header_and_absolute_uri() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "http://my-host-uri/path".parse().unwrap();
        hyper_request
            .headers_mut()
            .insert(headers::HOST, "my-host-header".parse().unwrap());

        let iron_request = Request::from_http(hyper_request, None, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Domain("my-host-uri"));
    }

    #[test]
    fn test_request_parse_ipv4_socket_only() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "/path".parse().unwrap();
        *hyper_request.version_mut() = HttpVersion::HTTP_10;

        let socket_addr = Some("1.2.3.4:80".parse().unwrap());
        let iron_request = Request::from_http(hyper_request, socket_addr, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Ipv4([1, 2, 3, 4].into()));
    }

    #[test]
    fn test_request_parse_ipv6_socket_only() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "/path".parse().unwrap();
        *hyper_request.version_mut() = HttpVersion::HTTP_10;

        let socket_addr = Some("[1:2:3:4:5:6:7:8]:80".parse().unwrap());
        let iron_request = Request::from_http(hyper_request, socket_addr, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(
            iron_request.url.host(),
            Ipv6([1, 2, 3, 4, 5, 6, 7, 8].into())
        );
    }

    #[test]
    fn test_request_parse_host_header_ipv4_socket_and_absolute_uri() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "http://my-host-uri/path".parse().unwrap();
        hyper_request
            .headers_mut()
            .insert(headers::HOST, "my-host-header".parse().unwrap());

        let socket_addr = Some("1.2.3.4:80".parse().unwrap());
        let iron_request = Request::from_http(hyper_request, socket_addr, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Domain("my-host-uri"));
    }
}
<commit_msg>Quick fix to handle query strings instead of outright ignoring them completely<commit_after>//! Iron's HTTP Request representation and associated methods.
use std::fmt::{self, Debug};
use std::net::SocketAddr;

use futures::Stream;

use http;
use http::version::Version as HttpVersion;

use method::Method;
use plugin::Extensible;
use typemap::{Key, TypeMap};

pub use hyper::Body;
pub use hyper::Request as HttpRequest;

#[cfg(test)]
use std::net::ToSocketAddrs;

pub use self::url::Url;

use error::HttpError;
use headers::{self, HeaderMap};
use {Plugin, Protocol, Set};

mod url;

/// The `Request` given to all `Middleware`.
///
/// Stores all the properties of the client's request plus
/// an `TypeMap` for data communication between middleware.
pub struct Request {
    /// The requested URL.
    pub url: Url,

    /// The local address of the request.
    pub local_addr: Option<SocketAddr>,

    /// The request headers.
    pub headers: HeaderMap,

    /// The request body as a reader.
    pub body: Option<Body>,

    /// The request method.
    pub method: Method,

    /// Extensible storage for data passed between middleware.
    pub extensions: TypeMap,

    /// The version of the HTTP protocol used.
    pub version: HttpVersion,

    _p: (),
}

impl Debug for Request {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(writeln!(f, "Request {{"));

        try!(writeln!(f, "    url: {:?}", self.url));
        try!(writeln!(f, "    method: {:?}", self.method));
        try!(writeln!(f, "    local_addr: {:?}", self.local_addr));

        try!(write!(f, "}}"));
        Ok(())
    }
}

impl Request {
    /// Create a request from an HttpRequest.
    ///
    /// This constructor consumes the HttpRequest.
    pub fn from_http(
        req: HttpRequest<Body>,
        local_addr: Option<SocketAddr>,
        protocol: &Protocol,
    ) -> Result<Request, String> {
        let (
            http::request::Parts {
                method,
                uri,
                version,
                headers,
                ..
            },
            body,
        ) = req.into_parts();

        let url = {
            let path = uri.path();

            let query = uri.query();

            let mut socket_ip = String::new();
            let (host, port) = if let Some(host) = uri.host() {
                (host, uri.port_part().map(|p| p.as_u16()))
            } else if let Some(host) = headers.get(headers::HOST).and_then(|h| h.to_str().ok()) {
                let mut parts = host.split(':');
                let hostname = parts.next().unwrap();
                let port = parts.next().and_then(|p| p.parse::<u16>().ok());
                (hostname, port)
            } else if version < HttpVersion::HTTP_11 {
                if let Some(local_addr) = local_addr {
                    match local_addr {
                        SocketAddr::V4(addr4) => socket_ip.push_str(&format!("{}", addr4.ip())),
                        SocketAddr::V6(addr6) => socket_ip.push_str(&format!("[{}]", addr6.ip())),
                    }
                    (socket_ip.as_ref(), Some(local_addr.port()))
                } else {
                    return Err("No fallback host specified".into());
                }
            } else {
                return Err("No host specified in request".into());
            };

            let url_string = if let Some(port) = port {
                if let Some(query) = query {
                    format!("{}://{}:{}{}?{}", protocol.name(), host, port, path, query)
                } else {
                    format!("{}://{}:{}{}", protocol.name(), host, port, path)
                }
            } else {
                if let Some(query) = query {
                    format!("{}://{}{}?{}", protocol.name(), host, path, query)
                } else {
                    format!("{}://{}{}", protocol.name(), host, path)
                }
            };

            match Url::parse(&url_string) {
                Ok(url) => url,
                Err(e) => return Err(format!("Couldn't parse requested URL: {}", e)),
            }
        };

        Ok(Request {
            url,
            local_addr,
            headers,
            body: Some(body),
            method,
            extensions: TypeMap::new(),
            version,
            _p: (),
        })
    }

    /// Get the contents of the body as a Vec<u8>
    ///
    /// This consumes the body future and turns it into Vec<u8>.  Note this should not be called
    /// from the main hyper thread, as it will potentially deadlock.
    pub fn get_body_contents(&mut self) -> Result<&Vec<u8>, HttpError> {
        if let Some(reader) = self.body.take() {
            let body = reader.wait().fold(Ok(Vec::new()), |r, input| {
                if let Ok(mut v) = r {
                    input.map(move |next_body_chunk| {
                        v.extend_from_slice(&next_body_chunk);
                        v
                    })
                } else {
                    r
                }
            });
            match body {
                Ok(body) => self.extensions.insert::<RequestBodyKey>(body),
                Err(e) => return Err(e),
            };
        }
        Ok(self.extensions.get::<RequestBodyKey>().unwrap())
    }

    #[cfg(test)]
    pub fn stub() -> Request {
        Request {
            url: Url::parse("http://www.rust-lang.org").unwrap(),
            local_addr: "localhost:3000".to_socket_addrs().unwrap().next(),
            headers: HeaderMap::new(),
            body: Some(Body::empty()),
            method: Method::GET,
            extensions: TypeMap::new(),
            version: HttpVersion::HTTP_11,
            _p: (),
        }
    }
}

struct RequestBodyKey;

impl Key for RequestBodyKey {
    type Value = Vec<u8>;
}

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

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

impl Plugin for Request {}
impl Set for Request {}

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

    use headers;

    use url_ext::Host::*;

    #[test]
    fn test_request_parse_absolute_uri() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "http://my-host/path".parse().unwrap();

        let iron_request = Request::from_http(hyper_request, None, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Domain("my-host"));
    }

    #[test]
    fn test_request_parse_host_header_only() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "/path".parse().unwrap();
        hyper_request
            .headers_mut()
            .insert(headers::HOST, "my-host".parse().unwrap());

        let iron_request = Request::from_http(hyper_request, None, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Domain("my-host"));
    }

    #[test]
    fn test_request_parse_host_header_and_absolute_uri() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "http://my-host-uri/path".parse().unwrap();
        hyper_request
            .headers_mut()
            .insert(headers::HOST, "my-host-header".parse().unwrap());

        let iron_request = Request::from_http(hyper_request, None, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Domain("my-host-uri"));
    }

    #[test]
    fn test_request_parse_ipv4_socket_only() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "/path".parse().unwrap();
        *hyper_request.version_mut() = HttpVersion::HTTP_10;

        let socket_addr = Some("1.2.3.4:80".parse().unwrap());
        let iron_request = Request::from_http(hyper_request, socket_addr, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Ipv4([1, 2, 3, 4].into()));
    }

    #[test]
    fn test_request_parse_ipv6_socket_only() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "/path".parse().unwrap();
        *hyper_request.version_mut() = HttpVersion::HTTP_10;

        let socket_addr = Some("[1:2:3:4:5:6:7:8]:80".parse().unwrap());
        let iron_request = Request::from_http(hyper_request, socket_addr, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(
            iron_request.url.host(),
            Ipv6([1, 2, 3, 4, 5, 6, 7, 8].into())
        );
    }

    #[test]
    fn test_request_parse_host_header_ipv4_socket_and_absolute_uri() {
        let mut hyper_request = HttpRequest::new(Body::empty());
        *hyper_request.method_mut() = Method::GET;
        *hyper_request.uri_mut() = "http://my-host-uri/path".parse().unwrap();
        hyper_request
            .headers_mut()
            .insert(headers::HOST, "my-host-header".parse().unwrap());

        let socket_addr = Some("1.2.3.4:80".parse().unwrap());
        let iron_request = Request::from_http(hyper_request, socket_addr, &Protocol::http())
            .expect("A valid Iron request");

        assert_eq!(iron_request.url.host(), Domain("my-host-uri"));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Tibor Benke <ihrwein@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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use handlebars::{Handlebars, Template};
use std::collections::BTreeMap;
use std::sync::Arc;

use message::Message;
use state::State;
use timer::TimerEvent;
use context::base::BaseContext;
use dispatcher::request::Request;
use dispatcher::response::ResponseSender;

const CONTEXT_ID: &'static str = ".context.id";

pub struct MapContext {
    base: BaseContext,
    map: BTreeMap<String, State>,
    context_id: Handlebars,
}

impl MapContext {
    pub fn new(base: BaseContext, context_id: Template) -> MapContext {
        let mut handlebars = Handlebars::new();
        handlebars.register_template(CONTEXT_ID, context_id);
        MapContext {
            base: base,
            map: BTreeMap::new(),
            context_id: handlebars,
        }
    }

    pub fn on_event(&mut self, event: Request, responder: &mut ResponseSender) {
        trace!("MapContext: received event");
        match event {
            Request::Timer(event) => self.on_timer(&event, responder),
            Request::Message(message) => self.on_message(message, responder),
            _ => {}
        }
    }

    #[allow(for_kv_map)]
    pub fn on_timer(&mut self, event: &TimerEvent, responder: &mut ResponseSender) {
        for (_, mut state) in &mut self.map {
            self.base.on_timer(event, &mut state, responder);
        }
        self.remove_closed_states();
    }

    fn get_closed_state_ids(&self) -> Vec<String> {
        self.map
            .iter()
            .filter_map(|(id, state)| {
                if state.is_open() {
                    None
                } else {
                    Some(id.clone())
                }
            })
            .collect::<Vec<String>>()
    }

    fn remove_closed_states(&mut self) {
        for id in self.get_closed_state_ids() {
            let _ = self.map.remove(&id);
        }
    }

    pub fn on_message(&mut self, event: Arc<Message>, responder: &mut ResponseSender) {
        self.update_state(event, responder);
        self.remove_closed_states();
    }

    fn update_state(&mut self, event: Arc<Message>, responder: &mut ResponseSender) {
        if let Ok(id) = self.context_id.render(CONTEXT_ID, event.values()) {
            let mut state = self.map.entry(id).or_insert_with(State::new);
            self.base.on_message(event, &mut state, responder);
        } else {
            error!("Failed to render the context-id: {:?}",
                   self.context_id.get_template(&CONTEXT_ID.to_owned()));
        }
    }

    #[allow(dead_code)]
    pub fn is_open(&self) -> bool {
        !self.map.is_empty()
    }

    pub fn patterns(&self) -> &[String] {
        &self.base.patterns
    }
}
<commit_msg>MapContext: change type of context_id field<commit_after>// Copyright (c) 2016 Tibor Benke <ihrwein@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. All files in the project carrying such notice may not be copied,
// modified, or distributed except according to those terms.

use std::collections::BTreeMap;
use std::sync::Arc;

use message::Message;
use state::State;
use timer::TimerEvent;
use context::base::BaseContext;
use dispatcher::request::Request;
use dispatcher::response::ResponseSender;

pub type ContextKey = Vec<(String, String)>;

pub struct MapContext {
    base: BaseContext,
    map: BTreeMap<ContextKey, State>,
    context_id: Vec<String>,
}

impl MapContext {
    pub fn new(base: BaseContext, context_id: Vec<String>) -> MapContext {
        MapContext {
            base: base,
            map: BTreeMap::new(),
            context_id: context_id,
        }
    }

    pub fn on_event(&mut self, event: Request, responder: &mut ResponseSender) {
        trace!("MapContext: received event");
        match event {
            Request::Timer(event) => self.on_timer(&event, responder),
            Request::Message(message) => self.on_message(message, responder),
            _ => {}
        }
    }

    #[allow(for_kv_map)]
    pub fn on_timer(&mut self, event: &TimerEvent, responder: &mut ResponseSender) {
        for (_, mut state) in &mut self.map {
            self.base.on_timer(event, &mut state, responder);
        }
        self.remove_closed_states();
    }

    fn get_closed_state_ids(&self) -> Vec<ContextKey> {
        self.map
            .iter()
            .filter_map(|(id, state)| {
                if state.is_open() {
                    None
                } else {
                    Some(id.clone())
                }
            })
            .collect::<Vec<ContextKey>>()
    }

    fn remove_closed_states(&mut self) {
        for id in self.get_closed_state_ids() {
            let _ = self.map.remove(&id);
        }
    }

    pub fn on_message(&mut self, event: Arc<Message>, responder: &mut ResponseSender) {
        self.update_state(event, responder);
        self.remove_closed_states();
    }

    fn update_state(&mut self, event: Arc<Message>, responder: &mut ResponseSender) {
        let key: ContextKey = self.context_id.iter().map(|key| {
                (key.to_owned(), event.get(&key).map_or_else(|| "".to_owned(), |value| value.to_owned()))
            }).collect();
        let mut state = self.map.entry(key).or_insert_with(State::new);
        self.base.on_message(event, &mut state, responder);
    }

    #[allow(dead_code)]
    pub fn is_open(&self) -> bool {
        !self.map.is_empty()
    }

    pub fn patterns(&self) -> &[String] {
        &self.base.patterns
    }
}
<|endoftext|>


<commit_before>use core::borrow::{Borrow, BorrowMut};
use core::cmp;
use core::fmt::{self, Debug};
use core::ops::{Deref, DerefMut};

#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;

use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};
use serde::ser::{Serialize, Serializer};

use crate::Bytes;

/// Wrapper around `Vec<u8>` to serialize and deserialize efficiently.
///
/// ```
/// use std::collections::HashMap;
/// use std::io;
///
/// use serde_bytes::ByteBuf;
///
/// fn deserialize_bytebufs() -> bincode::Result<()> {
///     let example_data = [
///         2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 116,
///         119, 111, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 111, 110, 101];
///
///     let map: HashMap<u32, ByteBuf> = bincode::deserialize(&example_data[..])?;
///
///     println!("{:?}", map);
///
///     Ok(())
/// }
/// #
/// # fn main() {
/// #     deserialize_bytebufs().unwrap();
/// # }
/// ```
#[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ByteBuf {
    bytes: Vec<u8>,
}

impl ByteBuf {
    /// Construct a new, empty `ByteBuf`.
    pub fn new() -> Self {
        ByteBuf::from(Vec::new())
    }

    /// Construct a new, empty `ByteBuf` with the specified capacity.
    pub fn with_capacity(cap: usize) -> Self {
        ByteBuf::from(Vec::with_capacity(cap))
    }

    /// Wrap existing bytes in a `ByteBuf`.
    pub fn from<T: Into<Vec<u8>>>(bytes: T) -> Self {
        ByteBuf {
            bytes: bytes.into(),
        }
    }

    /// Unwrap the vector of byte underlying this `ByteBuf`.
    pub fn into_inner(self) -> Vec<u8> {
        self.bytes
    }
}

impl Debug for ByteBuf {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(&self.bytes, f)
    }
}

impl From<Vec<u8>> for ByteBuf {
    fn from(bytes: Vec<u8>) -> Self {
        ByteBuf::from(bytes)
    }
}

impl AsRef<[u8]> for ByteBuf {
    fn as_ref(&self) -> &[u8] {
        &self.bytes
    }
}

impl AsMut<[u8]> for ByteBuf {
    fn as_mut(&mut self) -> &mut [u8] {
        &mut self.bytes
    }
}

impl Deref for ByteBuf {
    type Target = Vec<u8>;

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

impl DerefMut for ByteBuf {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.bytes
    }
}

impl Borrow<Bytes> for ByteBuf {
    fn borrow(&self) -> &Bytes {
        Bytes::new(&self.bytes)
    }
}

impl BorrowMut<Bytes> for ByteBuf {
    fn borrow_mut(&mut self) -> &mut Bytes {
        unsafe { &mut *(&mut self.bytes as &mut [u8] as *mut [u8] as *mut Bytes) }
    }
}

impl Serialize for ByteBuf {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_bytes(&self.bytes)
    }
}

struct ByteBufVisitor;

impl<'de> Visitor<'de> for ByteBufVisitor {
    type Value = ByteBuf;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("byte array")
    }

    fn visit_seq<V>(self, mut visitor: V) -> Result<ByteBuf, V::Error>
    where
        V: SeqAccess<'de>,
    {
        let len = cmp::min(visitor.size_hint().unwrap_or(0), 4096);
        let mut values = Vec::with_capacity(len);

        while let Some(value) = visitor.next_element()? {
            values.push(value);
        }

        Ok(ByteBuf::from(values))
    }

    fn visit_bytes<E>(self, v: &[u8]) -> Result<ByteBuf, E>
    where
        E: Error,
    {
        Ok(ByteBuf::from(v))
    }

    fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<ByteBuf, E>
    where
        E: Error,
    {
        Ok(ByteBuf::from(v))
    }

    fn visit_str<E>(self, v: &str) -> Result<ByteBuf, E>
    where
        E: Error,
    {
        Ok(ByteBuf::from(v))
    }

    fn visit_string<E>(self, v: String) -> Result<ByteBuf, E>
    where
        E: Error,
    {
        Ok(ByteBuf::from(v))
    }
}

impl<'de> Deserialize<'de> for ByteBuf {
    fn deserialize<D>(deserializer: D) -> Result<ByteBuf, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_byte_buf(ByteBufVisitor)
    }
}
<commit_msg>Remove unneeded From impl<commit_after>use core::borrow::{Borrow, BorrowMut};
use core::cmp;
use core::fmt::{self, Debug};
use core::ops::{Deref, DerefMut};

#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;

use serde::de::{Deserialize, Deserializer, Error, SeqAccess, Visitor};
use serde::ser::{Serialize, Serializer};

use crate::Bytes;

/// Wrapper around `Vec<u8>` to serialize and deserialize efficiently.
///
/// ```
/// use std::collections::HashMap;
/// use std::io;
///
/// use serde_bytes::ByteBuf;
///
/// fn deserialize_bytebufs() -> bincode::Result<()> {
///     let example_data = [
///         2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 116,
///         119, 111, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 111, 110, 101];
///
///     let map: HashMap<u32, ByteBuf> = bincode::deserialize(&example_data[..])?;
///
///     println!("{:?}", map);
///
///     Ok(())
/// }
/// #
/// # fn main() {
/// #     deserialize_bytebufs().unwrap();
/// # }
/// ```
#[derive(Clone, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ByteBuf {
    bytes: Vec<u8>,
}

impl ByteBuf {
    /// Construct a new, empty `ByteBuf`.
    pub fn new() -> Self {
        ByteBuf::from(Vec::new())
    }

    /// Construct a new, empty `ByteBuf` with the specified capacity.
    pub fn with_capacity(cap: usize) -> Self {
        ByteBuf::from(Vec::with_capacity(cap))
    }

    /// Wrap existing bytes in a `ByteBuf`.
    pub fn from<T: Into<Vec<u8>>>(bytes: T) -> Self {
        ByteBuf {
            bytes: bytes.into(),
        }
    }

    /// Unwrap the vector of byte underlying this `ByteBuf`.
    pub fn into_inner(self) -> Vec<u8> {
        self.bytes
    }
}

impl Debug for ByteBuf {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(&self.bytes, f)
    }
}

impl AsRef<[u8]> for ByteBuf {
    fn as_ref(&self) -> &[u8] {
        &self.bytes
    }
}

impl AsMut<[u8]> for ByteBuf {
    fn as_mut(&mut self) -> &mut [u8] {
        &mut self.bytes
    }
}

impl Deref for ByteBuf {
    type Target = Vec<u8>;

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

impl DerefMut for ByteBuf {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.bytes
    }
}

impl Borrow<Bytes> for ByteBuf {
    fn borrow(&self) -> &Bytes {
        Bytes::new(&self.bytes)
    }
}

impl BorrowMut<Bytes> for ByteBuf {
    fn borrow_mut(&mut self) -> &mut Bytes {
        unsafe { &mut *(&mut self.bytes as &mut [u8] as *mut [u8] as *mut Bytes) }
    }
}

impl Serialize for ByteBuf {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_bytes(&self.bytes)
    }
}

struct ByteBufVisitor;

impl<'de> Visitor<'de> for ByteBufVisitor {
    type Value = ByteBuf;

    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("byte array")
    }

    fn visit_seq<V>(self, mut visitor: V) -> Result<ByteBuf, V::Error>
    where
        V: SeqAccess<'de>,
    {
        let len = cmp::min(visitor.size_hint().unwrap_or(0), 4096);
        let mut values = Vec::with_capacity(len);

        while let Some(value) = visitor.next_element()? {
            values.push(value);
        }

        Ok(ByteBuf::from(values))
    }

    fn visit_bytes<E>(self, v: &[u8]) -> Result<ByteBuf, E>
    where
        E: Error,
    {
        Ok(ByteBuf::from(v))
    }

    fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<ByteBuf, E>
    where
        E: Error,
    {
        Ok(ByteBuf::from(v))
    }

    fn visit_str<E>(self, v: &str) -> Result<ByteBuf, E>
    where
        E: Error,
    {
        Ok(ByteBuf::from(v))
    }

    fn visit_string<E>(self, v: String) -> Result<ByteBuf, E>
    where
        E: Error,
    {
        Ok(ByteBuf::from(v))
    }
}

impl<'de> Deserialize<'de> for ByteBuf {
    fn deserialize<D>(deserializer: D) -> Result<ByteBuf, D::Error>
    where
        D: Deserializer<'de>,
    {
        deserializer.deserialize_byte_buf(ByteBufVisitor)
    }
}
<|endoftext|>


<commit_before>//! The collection keeps and maintains all audio data.
use super::{super::config, bktree::BKTree};

<<<<<<< HEAD
use id3::Tag as id3tag;
use libp2p_core::PeerId;
use mp4ameta::Tag as mp4tag;
=======
use id3::Tag;
use libp2p_core::PeerId;
>>>>>>> ada03fd151b74cd8222b0972ef2b83263fae9ee4
use std::{
    fs::{self, DirEntry}, // directory
    io,                   // reading files
    mem,
    path::Path,
    sync::{Arc as SArc, Mutex as SMutex},
<<<<<<< HEAD
    time::Duration,
=======
>>>>>>> ada03fd151b74cd8222b0972ef2b83263fae9ee4
};
use tree_magic;

static TOLERANCE: usize = 5;

struct AudioInfo {
<<<<<<< HEAD
    duration: Duration,
=======
    duration: u32,
>>>>>>> ada03fd151b74cd8222b0972ef2b83263fae9ee4
    album: String,
    path: String,
    //computer: String,
}

// todo: think over this, peer and max threads ... kick it out
//       threads are interesting to fill in unused threads in path
//       for string distance with rayon!
#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Container {
    bk_tree: BKTree<String, Box<AudioInfo>>,
}
impl Container {
    pub fn new() -> Self {
        Self {
            bk_tree: BKTree::new(),
        }
    }
}

pub struct Collection {
    who: Worker,
    /// This collection contains all data
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

<<<<<<< HEAD
#[allow(dead_code)]
struct CommonAudioInfo<'a> {
    title: &'a str,
    artist: &'a str,
    duration: Duration,
    album: Option<&'a str>,
    track: Option<u32>,
    album_artist: Option<&'a str>,
    genre: Option<&'a str>,
    disc: Option<u32>,
    total_discs: Option<u32>,
    total_tracks: Option<u32>,
    year: Option<i32>,
}

=======
>>>>>>> ada03fd151b74cd8222b0972ef2b83263fae9ee4
impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    memory: u64,
    files: FilesStat,
    threads: usize,
}

type FileFn =
    dyn Fn(&mut Collection, SArc<SMutex<Container>>, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            stats: Stats {
                memory: 0,
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(
        &mut self,
        container: SArc<SMutex<Container>>,
        dir: &Path,
        cb: &FileFn,
    ) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            // todo: go with free threads in the search with rayon
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(container.clone(), &path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, container.clone(), &entry, &mut loop_file_stats).or_else(
                        |io_error| {
                            warn!("{:?}", io_error);
                            Err(io_error)
                        },
                    )?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        data: SArc<SMutex<Container>>,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
<<<<<<< HEAD
            // some audio files have video-mimetype
            Some("audio") | Some("video") => {
=======
            Some("audio") => {
>>>>>>> ada03fd151b74cd8222b0972ef2b83263fae9ee4
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(data, &cb.path(), file_stats)
                            .or_else(|_| {
                                col.stats.files.faulty += 1;
                                file_stats.faulty += 1;
                                error!("ts: {:?}", filetype);
                                Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                            })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
<<<<<<< HEAD
            Some("text") | Some("application") | Some("image") => Ok(()),
=======
            // FIXME: video in taglib holds also oga which are audio indeed ...
            Some("text") | Some("application") | Some("image") | Some("video") => Ok(()),
>>>>>>> ada03fd151b74cd8222b0972ef2b83263fae9ee4
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(
        &mut self,
        data: SArc<SMutex<Container>>,
        cb: &Path,
        file_stats: &mut FilesStat,
    ) -> Result<(), ()> {
<<<<<<< HEAD
        let file_name = cb.to_str().unwrap();
        // todo: only one file handle for all libs, no read_from_path, etc but one and then handle
        id3tag::read_from_path(file_name)
            .and_then(|tag| {
                // write into common audio info that can be analyzed
                let id3tag = CommonAudioInfo {
                    title: tag.title().unwrap_or(""),
                    artist: tag.artist().unwrap_or(""),
                    duration: Duration::from_secs(tag.duration().unwrap_or(0) as u64),
                    album: tag.album(),
                    track: tag.track(),
                    album_artist: tag.album_artist(),
                    genre: tag.genre(),
                    disc: tag.disc(),
                    total_discs: tag.total_discs(),
                    total_tracks: tag.total_tracks(),
                    year: tag.year(),
                };
                self.analyze_tag(data.clone(), file_stats, file_name.to_string(), id3tag);
                Ok(())
            })
            .or_else(|e| match mp4tag::read_from_path(file_name) {
                Ok(tag) => {
                    // track info need extra treatment in mp4ameta
                    let try_track_info = tag.track_number();
                    let (track, total_tracks) = match try_track_info {
                        Some(good_track_info) => {
                            let (track, total_tracks) = good_track_info;
                            (Some(track as u32), Some(total_tracks as u32))
                        }
                        None => (None, None),
                    };
                    // year needs extra treatment
                    let year = tag
                        .year()
                        .map_or(None, |good_string| good_string.parse::<i32>().ok());

                    let mp4tag = CommonAudioInfo {
                        title: tag.title().unwrap_or(""),
                        artist: tag.artist().unwrap_or(""),
                        duration: Duration::from_secs(tag.duration().unwrap_or(0.0) as u64),
                        album: tag.album(),
                        track,
                        album_artist: tag.album_artist(),
                        genre: tag.genre(),
                        disc: None,        // no supported
                        total_discs: None, // no supported
                        total_tracks,
                        year,
                    };
                    self.analyze_tag(data, file_stats, file_name.to_string(), mp4tag);
                    trace!("found {}", tag.artist().unwrap_or("no good artist"));
                    Ok(())
                }
                Err(err) => Err(err),
            })
            .or_else(|e| {
=======
        Tag::read_from_path(cb.to_str().unwrap())
            .and_then(|tag| {
                let artist = tag.artist().unwrap_or("");
                let title = tag.title().unwrap_or("");
                let duration = tag.duration().unwrap_or(0);

                // audio book genre set is a strong indicator
                let _genre = tag.genre().unwrap_or("");
                //
                let album = tag.album().unwrap_or("");
                let _album_artist = tag.album_artist().unwrap_or("");
                // many discs and total discs is a strong indicator
                let _disc = tag.disc().unwrap_or(0);
                let _total_discs = tag.total_discs().unwrap_or(0);
                //
                // having a good path pattern is a strong indicator: cb.to_str().unwrap()

                let _total_tracks = tag.total_tracks().unwrap_or(0);
                let _track = tag.track().unwrap_or(0);
                let _year = tag.year().unwrap_or(0);

                self.stats.files.analyzed += 1;
                file_stats.analyzed += 1;

                let mut has_enough_information = true;

                // artist + song name is key for bktree
                if artist.is_empty() && title.is_empty() {
                    has_enough_information = false;
                }

                if has_enough_information {
                    let key = [artist, title].join(" ");

                    // a) filter numbers / remove
                    // b) use album name / substract it?

                    // todo: bktree and levenshtein distance, use cosine similarity instead
                    let ref mut locked_container = data.lock().unwrap();
                    let (vec_exact_match, vec_similarities) = locked_container.bk_tree.find(&key, TOLERANCE);
                    if !vec_similarities.is_empty() {
                        trace!("close: {:?} to {:?},", &vec_similarities, &key);
                    }
                    // if exact match, don't insert!!
                    if vec_exact_match.is_empty() {
                        // todo: also decide when to not add and insert then

                        let key = key.clone();
                        let value = Box::new(AudioInfo {
                            duration,
                            album: album.to_string(),
                            path: cb.to_str().unwrap().to_string()
                        });
                        let mem_size = mem::size_of_val(&key) + mem::size_of_val(&value);
                        locked_container.bk_tree.insert(
                            key,
                            value,
                        );
                        self.stats.memory += mem_size as u64;
                    } else {
                        // exact match with certain AudioInfo
                        trace!(
                            "for {:?}, {} exact matches found!",
                            &key,
                            vec_exact_match.len()
                        );
                        for audio_info in vec_exact_match {
                            let time_distance = audio_info.duration as i32 - duration as i32;
                            if time_distance.abs() > 0 {
                                trace!(
                                    "same: but time differs {} seconds with album name old '{}' and new: '{}'!",
                                    time_distance,
                                    audio_info.album,
                                    album
                                );
                            }
                        }
                    }
                }
                Ok(())
            })
            .or_else(|_| {
>>>>>>> ada03fd151b74cd8222b0972ef2b83263fae9ee4
                self.stats.files.faulty += 1;
                file_stats.faulty += 1;
                Ok(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             pathes/threads       : {nr_pathes:>width$}\n\
             memory               : {mem:>width$}kb\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            mem = self.stats.memory / 1000,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }
<<<<<<< HEAD

    fn analyze_tag(
        &mut self,
        data: SArc<SMutex<Container>>,
        file_stats: &mut FilesStat,
        file_name: String,
        audio_info: CommonAudioInfo,
    ) {
        self.stats.files.analyzed += 1;
        file_stats.analyzed += 1;

        // audio book genre set is a strong indicator
        // many discs and total discs is a strong indicator
        let mut has_enough_information = true;

        // artist + song name is key for bktree
        if audio_info.artist.is_empty() && audio_info.title.is_empty() {
            has_enough_information = false;
        }

        if has_enough_information {
            let key = [audio_info.artist, audio_info.title].join(" ");

            // a) filter numbers / remove
            // b) use album name / substract it?

            // todo: bktree and levenshtein distance, use cosine similarity instead
            let ref mut locked_container = data.lock().unwrap();
            let (vec_exact_match, vec_similarities) =
                locked_container.bk_tree.find(&key, TOLERANCE);
            if !vec_similarities.is_empty() {
                trace!("close: {:?} to {:?},", &vec_similarities, &key);
            }
            // if exact match, don't insert!!
            if vec_exact_match.is_empty() {
                // todo: also decide when to not add and insert then

                let key = key.clone();
                let value = Box::new(AudioInfo {
                    duration: audio_info.duration,
                    album: audio_info.album.unwrap_or("no album").to_string(),
                    path: file_name.to_string(),
                });
                let mem_size = mem::size_of_val(&key) + mem::size_of_val(&value);
                locked_container.bk_tree.insert(key, value);
                self.stats.memory += mem_size as u64;
            } else {
                // exact match with certain AudioInfo
                trace!(
                    "for {:?}, {} exact matches found!",
                    &key,
                    vec_exact_match.len()
                );
                for new_audio_info in vec_exact_match {
                    let time_distance = new_audio_info.duration - audio_info.duration;
                    if time_distance > Duration::from_secs(0) {
                        trace!(
                            "same: but time differs {:?} seconds with album name old '{}' and new: '{}'!",
                            time_distance,
                            new_audio_info.album,
                            audio_info.album.unwrap_or("no album").to_string()
                        );
                    }
                }
            }
        }
    }
=======
>>>>>>> ada03fd151b74cd8222b0972ef2b83263fae9ee4
}
<commit_msg>[collection] merge problem<commit_after>//! The collection keeps and maintains all audio data.
use super::{super::config, bktree::BKTree};

use id3::Tag as id3tag;
use libp2p_core::PeerId;
use mp4ameta::Tag as mp4tag;
use std::{
    fs::{self, DirEntry}, // directory
    io,                   // reading files
    mem,
    path::Path,
    sync::{Arc as SArc, Mutex as SMutex},
    time::Duration,
};
use tree_magic;

static TOLERANCE: usize = 5;

struct AudioInfo {
    duration: Duration,
    album: String,
    path: String,
    //computer: String,
}

// todo: think over this, peer and max threads ... kick it out
//       threads are interesting to fill in unused threads in path
//       for string distance with rayon!
#[allow(dead_code)]
struct Worker {
    /// identify them.
    peer_id: PeerId,
    max_threads: usize,
}

/// # Worker
/// the worker is supposed to be running on different machines
/// with one server and many clients
impl Worker {
    /// # Just new
    /// To identify how to comment
    /// # Arguments
    /// * 'id' - the identification (each will create an own hash)
    /// * 'max_threads' - how many threads can the worker create
    pub fn new(peer_id: PeerId, max_threads: usize) -> Worker {
        Worker {
            peer_id: peer_id,
            max_threads: max_threads,
        }
    }
}

pub struct Container {
    bk_tree: BKTree<String, Box<AudioInfo>>,
}
impl Container {
    pub fn new() -> Self {
        Self {
            bk_tree: BKTree::new(),
        }
    }
}

pub struct Collection {
    who: Worker,
    /// This collection contains all data
    stats: Stats,
}
/// Only some statistics
pub struct FilesStat {
    pub analyzed: u32,
    pub faulty: u32,
    pub searched: u32,
    pub other: u32,
}

#[allow(dead_code)]
struct CommonAudioInfo<'a> {
    title: &'a str,
    artist: &'a str,
    duration: Duration,
    album: Option<&'a str>,
    track: Option<u32>,
    album_artist: Option<&'a str>,
    genre: Option<&'a str>,
    disc: Option<u32>,
    total_discs: Option<u32>,
    total_tracks: Option<u32>,
    year: Option<i32>,
}

impl FilesStat {
    /// Adds stats from one to the other,
    /// used for combining different thread
    /// results.
    fn add(&mut self, other: &FilesStat) {
        self.analyzed += other.analyzed;
        self.faulty += other.faulty;
        self.searched += other.searched;
        self.other += other.other;
    }
}

struct Stats {
    memory: u64,
    files: FilesStat,
    threads: usize,
}

type FileFn =
    dyn Fn(&mut Collection, SArc<SMutex<Container>>, &DirEntry, &mut FilesStat) -> io::Result<()>;

impl Collection {
    /// Sets up the whole collection that books all threads.
    pub fn new(peer_id: &PeerId, num_threads: usize) -> Collection {
        Collection {
            who: Worker::new(peer_id.clone(), num_threads),
            stats: Stats {
                memory: 0,
                files: FilesStat {
                    analyzed: 0,
                    faulty: 0,
                    searched: 0,
                    other: 0,
                },
                threads: num_threads,
            },
        }
    }

    /// The function that runs from a certain path
    pub fn visit_path(
        &mut self,
        container: SArc<SMutex<Container>>,
        dir: &Path,
        cb: &FileFn,
    ) -> io::Result<FilesStat> {
        let mut file_stats = FilesStat {
            analyzed: 0,
            faulty: 0,
            searched: 0,
            other: 0,
        };

        if dir.is_dir() {
            // todo: go with free threads in the search with rayon
            for entry in fs::read_dir(dir)? {
                let mut loop_file_stats = &mut file_stats;

                let entry = entry?;
                let path = entry.path();
                if path.is_dir() {
                    let file_stats_loop = self.visit_path(container.clone(), &path, cb)?;
                    loop_file_stats.add(&file_stats_loop);
                } else {
                    cb(self, container.clone(), &entry, &mut loop_file_stats).or_else(
                        |io_error| {
                            warn!("{:?}", io_error);
                            Err(io_error)
                        },
                    )?
                }
            }
        }
        Ok(file_stats)
    }

    /// the function to check all files separately
    pub fn visit_files(
        col: &mut Collection,
        data: SArc<SMutex<Container>>,
        cb: &DirEntry,
        file_stats: &mut FilesStat,
    ) -> io::Result<()> {
        // count stats
        col.stats.files.searched += 1;
        file_stats.searched += 1;

        let filetype = tree_magic::from_filepath(&cb.path());
        let prefix = filetype.split("/").nth(0);
        match prefix {
            // some audio files have video-mimetype
            Some("audio") | Some("video") => {
                if let Some(suffix) = filetype.split("/").last() {
                    if config::data::IGNORE_AUDIO_FORMATS
                        .iter()
                        .any(|&s| s == suffix)
                    {
                        col.stats.files.other += 1;
                        file_stats.other += 1;
                        Ok(())
                    } else {
                        col.visit_audio_files(data, &cb.path(), file_stats)
                            .or_else(|_| {
                                col.stats.files.faulty += 1;
                                file_stats.faulty += 1;
                                error!("ts: {:?}", filetype);
                                Err(io::Error::new(io::ErrorKind::Other, "unknown audio file!"))
                            })
                    }
                } else {
                    col.stats.files.faulty += 1;
                    file_stats.faulty += 1;
                    Ok(())
                }
            }
            Some("text") | Some("application") | Some("image") => Ok(()),
            _ => {
                error!("[{:?}]{:?}", prefix, cb.path());
                col.stats.files.other += 1;
                file_stats.other += 1;
                Ok(())
            }
        }
    }

    /// Check the file and retrieve the meta-data info
    fn visit_audio_files(
        &mut self,
        data: SArc<SMutex<Container>>,
        cb: &Path,
        file_stats: &mut FilesStat,
    ) -> Result<(), ()> {
        let file_name = cb.to_str().unwrap();
        // todo: only one file handle for all libs, no read_from_path, etc but one and then handle
        id3tag::read_from_path(file_name)
            .and_then(|tag| {
                // write into common audio info that can be analyzed
                let id3tag = CommonAudioInfo {
                    title: tag.title().unwrap_or(""),
                    artist: tag.artist().unwrap_or(""),
                    duration: Duration::from_secs(tag.duration().unwrap_or(0) as u64),
                    album: tag.album(),
                    track: tag.track(),
                    album_artist: tag.album_artist(),
                    genre: tag.genre(),
                    disc: tag.disc(),
                    total_discs: tag.total_discs(),
                    total_tracks: tag.total_tracks(),
                    year: tag.year(),
                };
                self.analyze_tag(data.clone(), file_stats, file_name.to_string(), id3tag);
                Ok(())
            })
            .or_else(|e| match mp4tag::read_from_path(file_name) {
                Ok(tag) => {
                    // track info need extra treatment in mp4ameta
                    let try_track_info = tag.track_number();
                    let (track, total_tracks) = match try_track_info {
                        Some(good_track_info) => {
                            let (track, total_tracks) = good_track_info;
                            (Some(track as u32), Some(total_tracks as u32))
                        }
                        None => (None, None),
                    };
                    // year needs extra treatment
                    let year = tag
                        .year()
                        .map_or(None, |good_string| good_string.parse::<i32>().ok());

                    let mp4tag = CommonAudioInfo {
                        title: tag.title().unwrap_or(""),
                        artist: tag.artist().unwrap_or(""),
                        duration: Duration::from_secs(tag.duration().unwrap_or(0.0) as u64),
                        album: tag.album(),
                        track,
                        album_artist: tag.album_artist(),
                        genre: tag.genre(),
                        disc: None,        // no supported
                        total_discs: None, // no supported
                        total_tracks,
                        year,
                    };
                    self.analyze_tag(data, file_stats, file_name.to_string(), mp4tag);
                    trace!("found {}", tag.artist().unwrap_or("no good artist"));
                    Ok(())
                }
                Err(err) => Err(err),
            })
            .or_else(|e| {
                self.stats.files.faulty += 1;
                file_stats.faulty += 1;
                Ok(())
            })
    }

    pub fn print_stats(&self) {
        let output_string = format!(
            "This client's id     : {id:}\n\
             pathes/threads       : {nr_pathes:>width$}\n\
             memory               : {mem:>width$}kb\n\
             ----------------------     \n\
             analyzed files       : {files_analyzed:>width$}\n\
             searched files       : {files_searched:>width$}\n\
             irrelevant files     : {files_irrelevant:>width$}\n\
             faulty files         : {files_faulty:>width$}\n",
            id = self.who.peer_id.to_string().to_uppercase(),
            nr_pathes = self.stats.threads,
            mem = self.stats.memory / 1000,
            files_analyzed = self.stats.files.analyzed,
            files_searched = self.stats.files.searched,
            files_irrelevant = self.stats.files.other,
            files_faulty = self.stats.files.faulty, // awesome, really
            width = 5
        );

        info!("{}", output_string);
    }

    fn analyze_tag(
        &mut self,
        data: SArc<SMutex<Container>>,
        file_stats: &mut FilesStat,
        file_name: String,
        audio_info: CommonAudioInfo,
    ) {
        self.stats.files.analyzed += 1;
        file_stats.analyzed += 1;

        // audio book genre set is a strong indicator
        // many discs and total discs is a strong indicator
        let mut has_enough_information = true;

        // artist + song name is key for bktree
        if audio_info.artist.is_empty() && audio_info.title.is_empty() {
            has_enough_information = false;
        }

        if has_enough_information {
            let key = [audio_info.artist, audio_info.title].join(" ");

            // a) filter numbers / remove
            // b) use album name / substract it?

            // todo: bktree and levenshtein distance, use cosine similarity instead
            let ref mut locked_container = data.lock().unwrap();
            let (vec_exact_match, vec_similarities) =
                locked_container.bk_tree.find(&key, TOLERANCE);
            if !vec_similarities.is_empty() {
                trace!("close: {:?} to {:?},", &vec_similarities, &key);
            }
            // if exact match, don't insert!!
            if vec_exact_match.is_empty() {
                // todo: also decide when to not add and insert then

                let key = key.clone();
                let value = Box::new(AudioInfo {
                    duration: audio_info.duration,
                    album: audio_info.album.unwrap_or("no album").to_string(),
                    path: file_name.to_string(),
                });
                let mem_size = mem::size_of_val(&key) + mem::size_of_val(&value);
                locked_container.bk_tree.insert(key, value);
                self.stats.memory += mem_size as u64;
            } else {
                // exact match with certain AudioInfo
                trace!(
                    "for {:?}, {} exact matches found!",
                    &key,
                    vec_exact_match.len()
                );
                for new_audio_info in vec_exact_match {
                    let time_distance = new_audio_info.duration - audio_info.duration;
                    if time_distance > Duration::from_secs(0) {
                        trace!(
                            "same: but time differs {:?} seconds with album name old '{}' and new: '{}'!",
                            time_distance,
                            new_audio_info.album,
                            audio_info.album.unwrap_or("no album").to_string()
                        );
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::io;
use std::ffi::{CString, CStr, OsStr, AsOsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{PathBuf, Path};
use libc::{c_char, c_int, c_void, size_t};
use fuse::{fuse_args, fuse_mount_compat25};

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code, non_camel_case_types)]
mod libc {
    use libc::{c_char, c_int, c_void, size_t, ssize_t};

    /// Iovec data structure for readv and writev calls.
    #[repr(C)]
    pub struct iovec {
        pub iov_base: *const c_void,
        pub iov_len: size_t,
    }

    extern "system" {
        /// Read data from fd into multiple buffers
        pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
        /// Write data from multiple buffers to fd
        pub fn writev (fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t;

        pub fn realpath (file_name: *const c_char, resolved_name: *mut c_char) -> *const c_char;

        #[cfg(target_os = "macos")]
        pub fn unmount(dir: *const c_char, flags: c_int) -> c_int;
        #[cfg(not(target_os = "macos"))]
        pub fn umount(dir: *const c_char) -> c_int;
    }

    /// Max length for path names. 4096 should be reasonable safe (OS X uses 1024, Linux uses 4096)
    pub const PATH_MAX: usize = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
/// FIXME: Use Rust's realpath method once available in std (see also https://github.com/mozilla/rust/issues/11857)
fn real_path (path: &CStr) -> io::Result<CString> {
    let mut resolved: Vec<c_char> = Vec::with_capacity(libc::PATH_MAX);
    unsafe {
        if libc::realpath(path.as_ptr(), resolved.as_mut_ptr()).is_null() {
            Err(io::Error::last_os_error())
        } else {
            // Using CStr::from_ptr gets the correct string length via strlen()
            let cresolved = CStr::from_ptr(resolved.as_ptr());
            Ok(CString::new(cresolved.to_bytes()).unwrap())
        }
    }
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T> (options: &[&OsStr], f: F) -> T {
    let mut args: Vec<CString> = vec![CString::new("rust-fuse").unwrap()];
    args.extend(options.iter().map(|s| s.to_cstring().unwrap() ));
    let argptrs: Vec<*const i8> = args.iter().map(|s| s.as_ptr()).collect();
    f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
pub struct Channel {
    mountpoint: PathBuf,
    fd: c_int,
}

impl Channel {
    /// Create a new communication channel to the kernel driver by mounting the
    /// given path. The kernel driver will delegate filesystem operations of
    /// the given path to the channel. If the channel is dropped, the path is
    /// unmounted.
    pub fn new (mountpoint: &Path, options: &[&OsStr]) -> io::Result<Channel> {
        let mnt = try!(mountpoint.as_os_str().to_cstring().ok_or(
                io::Error::new(io::ErrorKind::InvalidInput, "invalid path")));
        real_path(&mnt).and_then(|mnt| {
            with_fuse_args(options, |args| {
                let fd = unsafe { fuse_mount_compat25(mnt.as_ptr(), args) };
                if fd < 0 {
                    Err(io::Error::last_os_error())
                } else {
                    let mountpoint = PathBuf::from(<OsStr as OsStrExt>::from_bytes(mnt.as_bytes()));
                    Ok(Channel { mountpoint: mountpoint, fd: fd })
                }
            })
        })
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.mountpoint
    }

    /// Receives data up to the capacity of the given buffer (can block).
    pub fn receive (&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        let rc = unsafe { ::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.capacity() as size_t) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            unsafe { buffer.set_len(rc as usize); }
            Ok(())
        }
    }

    /// Returns a sender object for this channel. The sender object can be
    /// used to send to the channel. Multiple sender objects can be used
    /// and they can safely be sent to other threads.
    pub fn sender (&self) -> ChannelSender {
        // Since write/writev syscalls are threadsafe, we can simply create
        // a sender by using the same fd and use it in other threads. Only
        // the channel closes the fd when dropped. If any sender is used after
        // dropping the channel, it'll return an EBADF error.
        ChannelSender { fd: self.fd }
    }
}

impl Drop for Channel {
    fn drop (&mut self) {
        // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
        // Close the communication channel to the kernel driver
        // (closing it before unnmount prevents sync unmount deadlock)
        unsafe { ::libc::close(self.fd); }
        // Unmount this channel's mount point
        let _ = unmount(&self.mountpoint);
    }
}

#[derive(Clone, Copy)]
pub struct ChannelSender {
    fd: c_int,
}

impl ChannelSender {
    /// Send all data in the slice of slice of bytes in a single write (can block).
    pub fn send (&self, buffer: &[&[u8]]) -> io::Result<()> {
        let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
            libc::iovec { iov_base: d.as_ptr() as *const c_void, iov_len: d.len() as size_t }
        }).collect();
        let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) -> io::Result<()> {
    // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally,
    // it attempts to call realpath, which in turn calls into the filesystem. So
    // if the filesystem returns an error, the unmount does not take place, with
    // no indication of the error available to the caller. So we call unmount
    // directly, which is what osxfuse does anyway, since we already converted
    // to the real path when we first mounted.
    #[cfg(target_os = "macos")] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::unmount(mnt.as_ptr(), 0) } }
    #[cfg(not(target_os = "macos"))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::umount(mnt.as_ptr()) } }

    let mnt = try!(mountpoint.as_os_str().to_cstring().ok_or(
            io::Error::new(io::ErrorKind::InvalidInput, "invalid path")));
    let rc = libc_umount(&mnt);
    if rc < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::with_fuse_args;
    use std::ffi::{CStr, OsStr};

    #[test]
    fn fuse_args () {
        with_fuse_args(&[OsStr::new("foo"), OsStr::new("bar")], |args| {
            assert_eq!(args.argc, 3);
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(0)).to_bytes() }, b"rust-fuse");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(1)).to_bytes() }, b"foo");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(2)).to_bytes() }, b"bar");
        });
    }
}
<commit_msg>Fall back to libfuse if Linux umount returns EPERM<commit_after>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::io;
use std::ffi::{CString, CStr, OsStr, AsOsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{PathBuf, Path};
use libc::{c_char, c_int, c_void, size_t};
use fuse::{fuse_args, fuse_mount_compat25};

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code, non_camel_case_types)]
mod libc {
    use libc::{c_char, c_int, c_void, size_t, ssize_t};

    /// Iovec data structure for readv and writev calls.
    #[repr(C)]
    pub struct iovec {
        pub iov_base: *const c_void,
        pub iov_len: size_t,
    }

    extern "system" {
        /// Read data from fd into multiple buffers
        pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
        /// Write data from multiple buffers to fd
        pub fn writev (fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t;

        pub fn realpath (file_name: *const c_char, resolved_name: *mut c_char) -> *const c_char;

        #[cfg(target_os = "macos")]
        pub fn unmount(dir: *const c_char, flags: c_int) -> c_int;
        #[cfg(not(target_os = "macos"))]
        pub fn umount(dir: *const c_char) -> c_int;
    }

    /// Max length for path names. 4096 should be reasonable safe (OS X uses 1024, Linux uses 4096)
    pub const PATH_MAX: usize = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
/// FIXME: Use Rust's realpath method once available in std (see also https://github.com/mozilla/rust/issues/11857)
fn real_path (path: &CStr) -> io::Result<CString> {
    let mut resolved: Vec<c_char> = Vec::with_capacity(libc::PATH_MAX);
    unsafe {
        if libc::realpath(path.as_ptr(), resolved.as_mut_ptr()).is_null() {
            Err(io::Error::last_os_error())
        } else {
            // Using CStr::from_ptr gets the correct string length via strlen()
            let cresolved = CStr::from_ptr(resolved.as_ptr());
            Ok(CString::new(cresolved.to_bytes()).unwrap())
        }
    }
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T> (options: &[&OsStr], f: F) -> T {
    let mut args: Vec<CString> = vec![CString::new("rust-fuse").unwrap()];
    args.extend(options.iter().map(|s| s.to_cstring().unwrap() ));
    let argptrs: Vec<*const i8> = args.iter().map(|s| s.as_ptr()).collect();
    f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
pub struct Channel {
    mountpoint: PathBuf,
    fd: c_int,
}

impl Channel {
    /// Create a new communication channel to the kernel driver by mounting the
    /// given path. The kernel driver will delegate filesystem operations of
    /// the given path to the channel. If the channel is dropped, the path is
    /// unmounted.
    pub fn new (mountpoint: &Path, options: &[&OsStr]) -> io::Result<Channel> {
        let mnt = try!(mountpoint.as_os_str().to_cstring().ok_or(
                io::Error::new(io::ErrorKind::InvalidInput, "invalid path")));
        real_path(&mnt).and_then(|mnt| {
            with_fuse_args(options, |args| {
                let fd = unsafe { fuse_mount_compat25(mnt.as_ptr(), args) };
                if fd < 0 {
                    Err(io::Error::last_os_error())
                } else {
                    let mountpoint = PathBuf::from(<OsStr as OsStrExt>::from_bytes(mnt.as_bytes()));
                    Ok(Channel { mountpoint: mountpoint, fd: fd })
                }
            })
        })
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.mountpoint
    }

    /// Receives data up to the capacity of the given buffer (can block).
    pub fn receive (&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        let rc = unsafe { ::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.capacity() as size_t) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            unsafe { buffer.set_len(rc as usize); }
            Ok(())
        }
    }

    /// Returns a sender object for this channel. The sender object can be
    /// used to send to the channel. Multiple sender objects can be used
    /// and they can safely be sent to other threads.
    pub fn sender (&self) -> ChannelSender {
        // Since write/writev syscalls are threadsafe, we can simply create
        // a sender by using the same fd and use it in other threads. Only
        // the channel closes the fd when dropped. If any sender is used after
        // dropping the channel, it'll return an EBADF error.
        ChannelSender { fd: self.fd }
    }
}

impl Drop for Channel {
    fn drop (&mut self) {
        // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
        // Close the communication channel to the kernel driver
        // (closing it before unnmount prevents sync unmount deadlock)
        unsafe { ::libc::close(self.fd); }
        // Unmount this channel's mount point
        let _ = unmount(&self.mountpoint);
    }
}

#[derive(Clone, Copy)]
pub struct ChannelSender {
    fd: c_int,
}

impl ChannelSender {
    /// Send all data in the slice of slice of bytes in a single write (can block).
    pub fn send (&self, buffer: &[&[u8]]) -> io::Result<()> {
        let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
            libc::iovec { iov_base: d.as_ptr() as *const c_void, iov_len: d.len() as size_t }
        }).collect();
        let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) -> io::Result<()> {
    // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally,
    // it attempts to call realpath, which in turn calls into the filesystem. So
    // if the filesystem returns an error, the unmount does not take place, with
    // no indication of the error available to the caller. So we call unmount
    // directly, which is what osxfuse does anyway, since we already converted
    // to the real path when we first mounted.

    #[cfg(target_os = "macos")] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::unmount(mnt.as_ptr(), 0) } }

    #[cfg(not(target_os = "macos"))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int {
        use fuse::fuse_unmount_compat22;
        use std::io::ErrorKind::PermissionDenied;

        let rc = unsafe { libc::umount(mnt.as_ptr()) };
        if rc < 0 && io::Error::last_os_error().kind() == PermissionDenied {
            // Linux always returns EPERM for non-root users.  We have to let the
            // library go through the setuid-root "fusermount -u" to unmount.
            unsafe { fuse_unmount_compat22(mnt.as_ptr()); }
            0
        } else {
            rc
        }
    }

    let mnt = try!(mountpoint.as_os_str().to_cstring().ok_or(
            io::Error::new(io::ErrorKind::InvalidInput, "invalid path")));
    let rc = libc_umount(&mnt);
    if rc < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::with_fuse_args;
    use std::ffi::{CStr, OsStr};

    #[test]
    fn fuse_args () {
        with_fuse_args(&[OsStr::new("foo"), OsStr::new("bar")], |args| {
            assert_eq!(args.argc, 3);
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(0)).to_bytes() }, b"rust-fuse");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(1)).to_bytes() }, b"foo");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(2)).to_bytes() }, b"bar");
        });
    }
}
<|endoftext|>


<commit_before>import azure::bindgen::*;
import azure::cairo;
import azure::cairo::bindgen::*;

fn on_osmain(f: fn~()) {
    let builder = task::builder();
    let opts = {
        sched: some({
            mode: task::osmain,
            native_stack_size: none
        })
        with task::get_opts(builder)
    };
    task::set_opts(builder, opts);
    task::run(builder, f);
}

fn main() {
    on_osmain {||
        sdl::init([
            sdl::init_video
        ]);
        let screen = sdl::video::set_video_mode(
            320, 200, 32,
            [sdl::video::swsurface],
            [sdl::video::doublebuf]);
        assert !screen.is_null();
        let sdl_surf = sdl::video::create_rgb_surface(
            [sdl::video::swsurface],
            320, 200, 32,
            0x00FF0000u32,
            0x0000FF00u32,
            0x000000FFu32,
            0x00000000u32
            );
        assert !sdl_surf.is_null();
        let cairo_surf = unsafe {
            cairo_image_surface_create_for_data(
                unsafe::reinterpret_cast((*sdl_surf).pixels),
                cairo::CAIRO_FORMAT_RGB24,
                (*sdl_surf).w,
                (*sdl_surf).h,
                (*sdl_surf).pitch as libc::c_int
            )
        };
        assert !cairo_surf.is_null();
        let azure_target = CreateDrawTargetForCairoSurface(cairo_surf);
        assert !azure_target.is_null();

        loop {
            let rect = {
                x: 200f as azure::Float,
                y: 200f as azure::Float,
                width: 100f as azure::Float,
                height: 100f as azure::Float
            };
            let color = {
                r: 0f as azure::Float,
                g: 1f as azure::Float,
                b: 0f as azure::Float,
                a: 1f as azure::Float
            };
            let pattern = CreateColorPattern(ptr::addr_of(color));
            DrawTargetFillRect(
                azure_target,
                ptr::addr_of(rect),
                unsafe { unsafe::reinterpret_cast(pattern) });
            ReleaseColorPattern(pattern);

            sdl::video::blit_surface(sdl_surf, ptr::null(),
                                     screen, ptr::null());
            sdl::video::flip(screen);
            sdl::event::poll_event {|_event|
            }
        }
        ReleaseDrawTarget(azure_target);
        cairo_surface_destroy(cairo_surf);
        sdl::quit();
    }
}<commit_msg>Exit on keydown<commit_after>import azure::bindgen::*;
import azure::cairo;
import azure::cairo::bindgen::*;

fn on_osmain(f: fn~()) {
    let builder = task::builder();
    let opts = {
        sched: some({
            mode: task::osmain,
            native_stack_size: none
        })
        with task::get_opts(builder)
    };
    task::set_opts(builder, opts);
    task::run(builder, f);
}

fn main() {
    on_osmain {||
        sdl::init([
            sdl::init_video
        ]);
        let screen = sdl::video::set_video_mode(
            320, 200, 32,
            [sdl::video::swsurface],
            [sdl::video::doublebuf]);
        assert !screen.is_null();
        let sdl_surf = sdl::video::create_rgb_surface(
            [sdl::video::swsurface],
            320, 200, 32,
            0x00FF0000u32,
            0x0000FF00u32,
            0x000000FFu32,
            0x00000000u32
            );
        assert !sdl_surf.is_null();
        let cairo_surf = unsafe {
            cairo_image_surface_create_for_data(
                unsafe::reinterpret_cast((*sdl_surf).pixels),
                cairo::CAIRO_FORMAT_RGB24,
                (*sdl_surf).w,
                (*sdl_surf).h,
                (*sdl_surf).pitch as libc::c_int
            )
        };
        assert !cairo_surf.is_null();
        let azure_target = CreateDrawTargetForCairoSurface(cairo_surf);
        assert !azure_target.is_null();

        loop {
            let rect = {
                x: 200f as azure::Float,
                y: 200f as azure::Float,
                width: 100f as azure::Float,
                height: 100f as azure::Float
            };
            let color = {
                r: 0f as azure::Float,
                g: 1f as azure::Float,
                b: 0f as azure::Float,
                a: 1f as azure::Float
            };
            let pattern = CreateColorPattern(ptr::addr_of(color));
            DrawTargetFillRect(
                azure_target,
                ptr::addr_of(rect),
                unsafe { unsafe::reinterpret_cast(pattern) });
            ReleaseColorPattern(pattern);

            sdl::video::blit_surface(sdl_surf, ptr::null(),
                                     screen, ptr::null());
            sdl::video::flip(screen);
            let mut mustbreak = false;
            sdl::event::poll_event {|event|
                alt event {
                  sdl::event::keyup_event(_) { mustbreak = true; }
                  _ { }
                }
            }
            if mustbreak { break }
        }
        ReleaseDrawTarget(azure_target);
        cairo_surface_destroy(cairo_surf);
        sdl::quit();
    }
}<|endoftext|>


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

//! Utilities for working with sessions.
//!
//! Sessions are created per action request and should be used as the only way
//! to communicate with the server. Sessions are responsible for handling action
//! errors (when something goes wrong) or notifying the server that the action
//! finished (by sending appropriate status signal).
//!
//! They also keep track of various statistics (such as number of transferred
//! bytes, action runtime, etc.) and stop the execution if they exceed limits
//! for a particular request.

mod demand;
mod error;
mod response;
mod sink;

use std::convert::TryInto;

use log::{error, info};

use crate::action;
use crate::message;
pub use self::demand::{Demand, Header, Payload};
pub use self::error::{Error, ParseError};
use self::response::{Response, Status};
pub use self::sink::{Sink};

/// A specialized `Result` type for sessions.
pub type Result<T> = std::result::Result<T, Error>;

/// Object associating a session with particular action request.
///
/// This is just a convenience type used to avoid threading large numbers of
/// parameters through different function calls.
///
/// Note that the payload can be quite large object, so it should not be stored
/// with the session itself. The payload should be consumed as soon as the
/// request is dispatched to a particular handler and parsed to a concrete type.
pub struct Task<'s, S: Session> {
    /// A session that this task should be run on.
    pub session: &'s mut S,
    /// Serialized data of the action request.
    pub payload: Payload,
}

impl<'s, S: Session> Task<'s, S> {

    /// Executes the task with a particular action handler.
    pub fn execute<R, H>(self, handler: H) -> Result<()>
    where
        R: action::Request,
        H: FnOnce(&mut S, R) -> Result<()>,
    {
        let request = self.payload.parse()?;
        handler(self.session, request)
    }
}

/// Processes given message, handling all errors.
///
/// This function takes a message from the GRR server and interprets it as an
/// action request. It creates a new session object and dispatches the request
/// to an appropriate action handler. Once the action finishes, the function
/// will send a final status message, informing the server about a success
/// or a failure.
///
/// Note that if action execution fails, this function deals with all the errors
/// by sending appropriate information to the server (if possible), logging them
/// and failing hard if a critical error (e.g. communication failure) occurred.
pub fn handle<M>(message: M)
where
    M: TryInto<Demand, Error=ParseError>,
{
    let demand = match message.try_into() {
        Ok(demand) => {
            info!("requested to execute the '{}' action", demand.action);
            demand
        }
        Err(error) => {
            error!("failed to parse the message: {}", error);
            return;
        }
    };

    let mut session = Action::from_demand(&demand);

    let result = action::dispatch(&demand.action, Task {
        session: &mut session,
        payload: demand.payload,
    });

    if let Err(ref error) = result {
        error!("failed to execute the '{}' action: {}", demand.action, error);
    } else {
        info!("finished executing the '{}' action", demand.action);
    }

    let status = Status {
        session_id: demand.header.session_id,
        request_id: demand.header.request_id,
        response_id: session.next_response_id,
        result: result,
    };

    let message = match status.try_into() {
        Ok(message) => message,
        Err(error) => {
            // If we cannot encode the final status message, there is nothing
            // we can do to notify the server, as status is responsible for
            // reporting errors. We can only log the error and carry on.
            error!("failed to encode status message: {}", error);
            return;
        }
    };

    message::send(message);
}

/// Abstraction for various kinds of sessions.
pub trait Session {
    /// Sends a reply to the flow that call the action.
    fn reply<R>(&mut self, response: R) -> Result<()>
    where R: action::Response;

    /// Sends a message to a particular sink.
    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where R: action::Response;
}

/// A session type for unrequested action executions.
///
/// Certain kind of actions are executed not only when a server flow decides to
/// do so, but also upon particular kind of events (e.g. the agent's startup).
/// In such cases, when one needs to trigger action execution manually, ad-hoc
/// sessions should be used.
pub struct Adhoc;

impl Session for Adhoc {

    // TODO: Session trait should be probably split into two traits and then
    // make the actions that do not care about the `reply` method implement the
    // simpler one.
    fn reply<R>(&mut self, response: R) -> Result<()>
    where
        R: action::Response,
    {
        error!("attempted to reply to an ad-hoc session, dropping response");
        drop(response);

        Ok(())
    }

    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where
        R: action::Response,
    {
        send(sink.wrap(response))?;

        Ok(())
    }
}

/// A session type for ordinary action requests.
///
/// This is a normal session type that that is associated with some flow on the
/// server. It keeps track of the responses it sends and collects statistics
/// about network and runtime utilization to kill the action if it is needed.
pub struct Action {
    header: Header,
    next_response_id: u64,
}

impl Action {

    /// Constructs a new session for the given `demand` object.
    pub fn from_demand(demand: &Demand) -> Action {
        Action {
            header: demand.header.clone(),
            next_response_id: 0,
        }
    }

    /// Wraps an action response to a session-specific response.
    fn wrap<R>(&self, response: R) -> Response<R>
    where
        R: action::Response
    {
        Response {
            session_id: self.header.session_id.clone(),
            request_id: Some(self.header.request_id),
            response_id: Some(self.next_response_id),
            data: response,
        }
    }
}

impl Session for Action {

    fn reply<R: action::Response>(&mut self, response: R) -> Result<()> {
        send(self.wrap(response))?;
        self.next_response_id += 1;

        Ok(())
    }

    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where
        R: action::Response,
    {
        send(sink.wrap(response))?;

        Ok(())
    }
}

/// Sends a session response to the server.
///
/// Note that this function is not exposed on purpose. Actions should send
/// responses through session objects which introduce a layer of safety. `send`
/// is a low-level utility supposed to be used internally.
fn send<R>(response: Response<R>) -> Result<()>
where
    R: action::Response,
{
    let message = response.try_into()?;
    message::send(message);

    Ok(())
}
<commit_msg>Move status creation to an action session method.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! Utilities for working with sessions.
//!
//! Sessions are created per action request and should be used as the only way
//! to communicate with the server. Sessions are responsible for handling action
//! errors (when something goes wrong) or notifying the server that the action
//! finished (by sending appropriate status signal).
//!
//! They also keep track of various statistics (such as number of transferred
//! bytes, action runtime, etc.) and stop the execution if they exceed limits
//! for a particular request.

mod demand;
mod error;
mod response;
mod sink;

use std::convert::TryInto;

use log::{error, info};

use crate::action;
use crate::message;
pub use self::demand::{Demand, Header, Payload};
pub use self::error::{Error, ParseError};
use self::response::{Response, Status};
pub use self::sink::{Sink};

/// A specialized `Result` type for sessions.
pub type Result<T> = std::result::Result<T, Error>;

/// Object associating a session with particular action request.
///
/// This is just a convenience type used to avoid threading large numbers of
/// parameters through different function calls.
///
/// Note that the payload can be quite large object, so it should not be stored
/// with the session itself. The payload should be consumed as soon as the
/// request is dispatched to a particular handler and parsed to a concrete type.
pub struct Task<'s, S: Session> {
    /// A session that this task should be run on.
    pub session: &'s mut S,
    /// Serialized data of the action request.
    pub payload: Payload,
}

impl<'s, S: Session> Task<'s, S> {

    /// Executes the task with a particular action handler.
    pub fn execute<R, H>(self, handler: H) -> Result<()>
    where
        R: action::Request,
        H: FnOnce(&mut S, R) -> Result<()>,
    {
        let request = self.payload.parse()?;
        handler(self.session, request)
    }
}

/// Processes given message, handling all errors.
///
/// This function takes a message from the GRR server and interprets it as an
/// action request. It creates a new session object and dispatches the request
/// to an appropriate action handler. Once the action finishes, the function
/// will send a final status message, informing the server about a success
/// or a failure.
///
/// Note that if action execution fails, this function deals with all the errors
/// by sending appropriate information to the server (if possible), logging them
/// and failing hard if a critical error (e.g. communication failure) occurred.
pub fn handle<M>(message: M)
where
    M: TryInto<Demand, Error=ParseError>,
{
    let demand = match message.try_into() {
        Ok(demand) => {
            info!("requested to execute the '{}' action", demand.action);
            demand
        }
        Err(error) => {
            error!("failed to parse the message: {}", error);
            return;
        }
    };

    let mut session = Action::from_demand(&demand);

    let result = action::dispatch(&demand.action, Task {
        session: &mut session,
        payload: demand.payload,
    });

    if let Err(ref error) = result {
        error!("failed to execute the '{}' action: {}", demand.action, error);
    } else {
        info!("finished executing the '{}' action", demand.action);
    }

    let message = match session.status(result).try_into() {
        Ok(message) => message,
        Err(error) => {
            // If we cannot encode the final status message, there is nothing
            // we can do to notify the server, as status is responsible for
            // reporting errors. We can only log the error and carry on.
            error!("failed to encode status message: {}", error);
            return;
        }
    };

    message::send(message);
}

/// Abstraction for various kinds of sessions.
pub trait Session {
    /// Sends a reply to the flow that call the action.
    fn reply<R>(&mut self, response: R) -> Result<()>
    where R: action::Response;

    /// Sends a message to a particular sink.
    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where R: action::Response;
}

/// A session type for unrequested action executions.
///
/// Certain kind of actions are executed not only when a server flow decides to
/// do so, but also upon particular kind of events (e.g. the agent's startup).
/// In such cases, when one needs to trigger action execution manually, ad-hoc
/// sessions should be used.
pub struct Adhoc;

impl Session for Adhoc {

    // TODO: Session trait should be probably split into two traits and then
    // make the actions that do not care about the `reply` method implement the
    // simpler one.
    fn reply<R>(&mut self, response: R) -> Result<()>
    where
        R: action::Response,
    {
        error!("attempted to reply to an ad-hoc session, dropping response");
        drop(response);

        Ok(())
    }

    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where
        R: action::Response,
    {
        send(sink.wrap(response))?;

        Ok(())
    }
}

/// A session type for ordinary action requests.
///
/// This is a normal session type that that is associated with some flow on the
/// server. It keeps track of the responses it sends and collects statistics
/// about network and runtime utilization to kill the action if it is needed.
pub struct Action {
    header: Header,
    next_response_id: u64,
}

impl Action {

    /// Constructs a new session for the given `demand` object.
    pub fn from_demand(demand: &Demand) -> Action {
        Action {
            header: demand.header.clone(),
            next_response_id: 0,
        }
    }

    /// Wraps an action response to a session-specific response.
    fn wrap<R>(&self, response: R) -> Response<R>
    where
        R: action::Response
    {
        Response {
            session_id: self.header.session_id.clone(),
            request_id: Some(self.header.request_id),
            response_id: Some(self.next_response_id),
            data: response,
        }
    }

    /// Wraps an action result to a session-specific status response.
    ///
    /// Note that this method consumes the session. The reason for this is that
    /// status response should be obtained as the very last response and no
    /// further replies should be possible after that.
    fn status(self, result: Result<()>) -> Status {
        Status {
            session_id: self.header.session_id,
            request_id: self.header.request_id,
            response_id: self.next_response_id,
            result: result,
        }
    }
}

impl Session for Action {

    fn reply<R: action::Response>(&mut self, response: R) -> Result<()> {
        send(self.wrap(response))?;
        self.next_response_id += 1;

        Ok(())
    }

    fn send<R>(&mut self, sink: Sink, response: R) -> Result<()>
    where
        R: action::Response,
    {
        send(sink.wrap(response))?;

        Ok(())
    }
}

/// Sends a session response to the server.
///
/// Note that this function is not exposed on purpose. Actions should send
/// responses through session objects which introduce a layer of safety. `send`
/// is a low-level utility supposed to be used internally.
fn send<R>(response: Response<R>) -> Result<()>
where
    R: action::Response,
{
    let message = response.try_into()?;
    message::send(message);

    Ok(())
}
<|endoftext|>