<commit_before>extern crate git2;
const TMP_NAME: &'static str = "tmp_fd2db5f8_bac2_4a1e_9487_4ac3414788aa";

use git2::*;
use std::process::Command;
use std::path::Path;
use shell::Shell;
use super::RepoHost;
use std::collections::HashMap;

pub fn module_ref(module: &str) -> String
{
    format!("refs/centralgit/splited/{}/split", module)
}

pub struct Scratch<'a>
{
    pub repo: Repository,
    pub host: &'a RepoHost,
}

impl<'a> Scratch<'a>
{
    pub fn new(path: &Path, host: &'a RepoHost) -> Scratch<'a>
    {
        Scratch {
            repo: Repository::init_bare(&path).expect("could not init scratch"),
            host: host,
        }
    }

    pub fn tracking(&self, module: &str, branch: &str) -> Option<Object>
    {
        let remote_name = format!("{}", module);
        let fetch_url = self.host.local_path(&module);
        let mut remote = if let Ok(remote) = self.repo.find_remote(&remote_name) {
            remote
        }
        else {
            debug!("==== create remote (remote_name:{}, remote_url:{})",
                   &remote_name,
                   &fetch_url);
            self.repo.remote(&remote_name, &fetch_url).expect("can't create remote")
        };

        let rs = remote.get_refspec(0).unwrap().str().unwrap().to_string();
        if let Ok(_) = remote.fetch(&[&rs], None, None) {
            return self.repo
                .revparse_single(&format!("remotes/{}/{}", module, branch))
                .ok();
        }
        else {
            return None;
        }
    }

    fn call_git(self: &Scratch<'a>, cmd: &str) -> Result<String, Error>
    {
        let args: Vec<&str> = cmd.split(" ").collect();
        let repo_path = &self.repo.path();
        let mut c = Command::new("git");
        c.current_dir(&repo_path);
        c.env("GIT_DIR", repo_path.as_os_str());
        c.args(&args);

        let output = c.output()
            .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
        Ok(String::from_utf8(output.stderr).expect("cannot decode utf8"))
    }

    // force push of the new revision-object to temp repo
    pub fn transfer(&self, rev: &str, source: &Path) -> Object
    {
        // TODO: implement using libgit
        let target = &self.repo.path();
        let shell = Shell { cwd: source.to_path_buf() };
        // create tmp branch
        shell.command(&format!("git branch -f {} {}", TMP_NAME, rev));
        // force push
        shell.command(&format!("git push --force {} {}",
                               &target.to_string_lossy(),
                               TMP_NAME));
        // delete tmp branch
        shell.command(&format!("git branch -D {}", TMP_NAME));

        let obj = self.repo.revparse_single(rev).expect("can't find transfered ref");
        return obj;
    }

    // takes everything from base except it's tree and replaces it with the tree
    // given
    pub fn rewrite(&self, base: &Commit, parents: &[&Commit], tree: &Tree) -> Oid
    {
        if parents.len() == 0 {
            ::std::fs::remove_file(self.repo.path().join("HEAD")).expect("can't remove HEAD");
        }
        else {
            self.repo.set_head_detached(parents[0].id()).expect("rewrite: can't detach head");
        }
        self.repo
            .commit(Some("HEAD"),
                    &base.author(),
                    &base.committer(),
                    &base.message().unwrap_or("no message"),
                    tree,
                    parents)
            .expect("rewrite: can't commit")
    }

    pub fn push(&self, oid: Oid, module: &str, target: &str) -> String
    {
        let commit = &self.repo.find_commit(oid).expect("can't find commit");
        self.repo.set_head_detached(commit.id()).expect("can't detach HEAD");
        let cmd = format!("push {} HEAD:{}", self.host.remote_url(module), target);
        let output = self.call_git(&cmd).expect("can't push");
        debug!("push: {}\n{}", cmd, output);
        format!("{}", output)
    }

    fn subtree(&self, tree: &Tree, path: &Path) -> Option<Tree>
    {
        if let Some(oid) = tree.get_path(path).map(|x| x.id()).ok() {
            return self.repo.find_tree(oid).ok();
        }
        else {
            return None;
        }
    }

    fn replace_child(&self, child: &Path, subtree: Oid, full_tree: Tree) -> Tree
    {
        let full_tree_id = {
            let mut builder = self.repo
                .treebuilder(Some(&full_tree))
                .expect("replace_child: can't create treebuilder");
            builder.insert(child, subtree, 0o0040000) // GIT_FILEMODE_TREE
                .expect("replace_child: can't insert tree");
            builder.write().expect("replace_child: can't write tree")
        };
        return self.repo.find_tree(full_tree_id).expect("replace_child: can't find new tree");
    }

    pub fn replace_subtree(&self, path: &Path, subtree: Oid, full_tree: Tree) -> Tree
    {
        if path.components().count() == 1 {
            return self.replace_child(path, subtree, full_tree);
        }
        else {
            let name = Path::new(path.file_name().expect("no module name"));
            let path = path.parent().expect("module not in subdir");

            let st = self.subtree(&full_tree, path).unwrap();
            let tree = self.replace_child(name, subtree, st);

            return self.replace_subtree(path, tree.id(), full_tree);
        }
    }

    pub fn split_subdir(&self, module: &str, newrev: Oid) -> Option<Oid>
    {
        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(newrev).expect("walk.push");
            walk
        };

        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");

            let new_tree = if let Ok(tree_entry) = tree.get_path(&Path::new(&module)) {
                self.repo.find_tree(tree_entry.id()).expect("central_submit: can't find tree")
            }
            else {
                continue;
            };

            let mut parents = vec![];
            for parent in commit.parents() {
                let parent = parent.id();
                if let Some(&parent) = map.get(&parent) {

                    let parent = self.repo.find_commit(parent).unwrap();
                    if new_tree.id() == parent.tree().unwrap().id() {
                        map.insert(commit.id(), parent.id());
                        continue 'walk;
                    }
                    parents.push(parent);
                }
            }

            let mut p = vec![];
            for parent in &parents { p.push(parent); }

            map.insert(commit.id(), self.rewrite(&commit, &p, &new_tree));
        }

        return map.get(&newrev).map(|&id|{id});
    }

    pub fn find_all_subdirs(&self, tree: &Tree) -> Vec<String>
    {
        let mut sd = vec![];
        for item in tree {
            if let Ok(st) = self.repo.find_tree(item.id()) {
                let name = item.name().unwrap();
                if !name.starts_with(".") {
                    sd.push(name.to_string());
                    for r in self.find_all_subdirs(&st) {
                        sd.push(format!("{}/{}", name, r));
                    }
                }
            }
        }
        return sd;
    }
}
<commit_msg>treat 0,1,2 parents as distinct cases<commit_after>extern crate git2;
const TMP_NAME: &'static str = "tmp_fd2db5f8_bac2_4a1e_9487_4ac3414788aa";

use git2::*;
use std::process::Command;
use std::path::Path;
use shell::Shell;
use super::RepoHost;
use std::collections::HashMap;

pub fn module_ref(module: &str) -> String
{
    format!("refs/centralgit/splited/{}/split", module)
}

pub struct Scratch<'a>
{
    pub repo: Repository,
    pub host: &'a RepoHost,
}

impl<'a> Scratch<'a>
{
    pub fn new(path: &Path, host: &'a RepoHost) -> Scratch<'a>
    {
        Scratch {
            repo: Repository::init_bare(&path).expect("could not init scratch"),
            host: host,
        }
    }

    pub fn tracking(&self, module: &str, branch: &str) -> Option<Object>
    {
        let remote_name = format!("{}", module);
        let fetch_url = self.host.local_path(&module);
        let mut remote = if let Ok(remote) = self.repo.find_remote(&remote_name) {
            remote
        }
        else {
            debug!("==== create remote (remote_name:{}, remote_url:{})",
                   &remote_name,
                   &fetch_url);
            self.repo.remote(&remote_name, &fetch_url).expect("can't create remote")
        };

        let rs = remote.get_refspec(0).unwrap().str().unwrap().to_string();
        if let Ok(_) = remote.fetch(&[&rs], None, None) {
            return self.repo
                .revparse_single(&format!("remotes/{}/{}", module, branch))
                .ok();
        }
        else {
            return None;
        }
    }

    fn call_git(self: &Scratch<'a>, cmd: &str) -> Result<String, Error>
    {
        let args: Vec<&str> = cmd.split(" ").collect();
        let repo_path = &self.repo.path();
        let mut c = Command::new("git");
        c.current_dir(&repo_path);
        c.env("GIT_DIR", repo_path.as_os_str());
        c.args(&args);

        let output = c.output()
            .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
        Ok(String::from_utf8(output.stderr).expect("cannot decode utf8"))
    }

    // force push of the new revision-object to temp repo
    pub fn transfer(&self, rev: &str, source: &Path) -> Object
    {
        // TODO: implement using libgit
        let target = &self.repo.path();
        let shell = Shell { cwd: source.to_path_buf() };
        // create tmp branch
        shell.command(&format!("git branch -f {} {}", TMP_NAME, rev));
        // force push
        shell.command(&format!("git push --force {} {}",
                               &target.to_string_lossy(),
                               TMP_NAME));
        // delete tmp branch
        shell.command(&format!("git branch -D {}", TMP_NAME));

        let obj = self.repo.revparse_single(rev).expect("can't find transfered ref");
        return obj;
    }

    // takes everything from base except it's tree and replaces it with the tree
    // given
    pub fn rewrite(&self, base: &Commit, parents: &[&Commit], tree: &Tree) -> Oid
    {
        if parents.len() == 0 {
            ::std::fs::remove_file(self.repo.path().join("HEAD")).expect("can't remove HEAD");
        }
        else {
            self.repo.set_head_detached(parents[0].id()).expect("rewrite: can't detach head");
        }
        self.repo
            .commit(Some("HEAD"),
                    &base.author(),
                    &base.committer(),
                    &base.message().unwrap_or("no message"),
                    tree,
                    parents)
            .expect("rewrite: can't commit")
    }

    pub fn push(&self, oid: Oid, module: &str, target: &str) -> String
    {
        let commit = &self.repo.find_commit(oid).expect("can't find commit");
        self.repo.set_head_detached(commit.id()).expect("can't detach HEAD");
        let cmd = format!("push {} HEAD:{}", self.host.remote_url(module), target);
        let output = self.call_git(&cmd).expect("can't push");
        debug!("push: {}\n{}", cmd, output);
        format!("{}", output)
    }

    fn subtree(&self, tree: &Tree, path: &Path) -> Option<Tree>
    {
        if let Some(oid) = tree.get_path(path).map(|x| x.id()).ok() {
            return self.repo.find_tree(oid).ok();
        }
        else {
            return None;
        }
    }

    fn replace_child(&self, child: &Path, subtree: Oid, full_tree: Tree) -> Tree
    {
        let full_tree_id = {
            let mut builder = self.repo
                .treebuilder(Some(&full_tree))
                .expect("replace_child: can't create treebuilder");
            builder.insert(child, subtree, 0o0040000) // GIT_FILEMODE_TREE
                .expect("replace_child: can't insert tree");
            builder.write().expect("replace_child: can't write tree")
        };
        return self.repo.find_tree(full_tree_id).expect("replace_child: can't find new tree");
    }

    pub fn replace_subtree(&self, path: &Path, subtree: Oid, full_tree: Tree) -> Tree
    {
        if path.components().count() == 1 {
            return self.replace_child(path, subtree, full_tree);
        }
        else {
            let name = Path::new(path.file_name().expect("no module name"));
            let path = path.parent().expect("module not in subdir");

            let st = self.subtree(&full_tree, path).unwrap();
            let tree = self.replace_child(name, subtree, st);

            return self.replace_subtree(path, tree.id(), full_tree);
        }
    }

    pub fn split_subdir(&self, module: &str, newrev: Oid) -> Option<Oid>
    {
        let walk = {
            let mut walk = self.repo.revwalk().expect("walk: can't create revwalk");
            walk.set_sorting(SORT_REVERSE | SORT_TOPOLOGICAL);
            walk.push(newrev).expect("walk.push");
            walk
        };

        let mut map = HashMap::<Oid, Oid>::new();

        'walk: for commit in walk {
            let commit = self.repo.find_commit(commit.unwrap()).unwrap();
            let tree = commit.tree().expect("commit has no tree");

            let new_tree = if let Ok(tree_entry) = tree.get_path(&Path::new(&module)) {
                self.repo.find_tree(tree_entry.id()).expect("central_submit: can't find tree")
            }
            else {
                continue 'walk;
            };

            match commit.parents().count() {
                2 => {
                    let parent1 = commit.parents().nth(0).unwrap().id();
                    let parent2 = commit.parents().nth(1).unwrap().id();
                    if let (Some(&parent1), Some(&parent2)) = (map.get(&parent1),
                                                               map.get(&parent2)) {
                        let parent1 = self.repo.find_commit(parent1).unwrap();
                        let parent2 = self.repo.find_commit(parent2).unwrap();
                        if new_tree.id() == parent1.tree().unwrap().id() {
                            map.insert(commit.id(), parent1.id());
                            continue 'walk;
                        }
                        if new_tree.id() == parent2.tree().unwrap().id() {
                            map.insert(commit.id(), parent2.id());
                            continue 'walk;
                        }
                        map.insert(commit.id(),
                                   self.rewrite(&commit, &[&parent1, &parent2], &new_tree));
                        continue 'walk;
                    }
                }
                1 => {
                    let parent = commit.parents().nth(0).unwrap().id();
                    if let Some(&parent) = map.get(&parent) {
                        let parent = self.repo.find_commit(parent).unwrap();
                        if new_tree.id() == parent.tree().unwrap().id() {
                            map.insert(commit.id(), parent.id());
                            continue 'walk;
                        }
                        map.insert(commit.id(), self.rewrite(&commit, &[&parent], &new_tree));
                        continue 'walk;
                    }
                }
                _ => {}
            }

            map.insert(commit.id(), self.rewrite(&commit, &[], &new_tree));
        }

        return map.get(&newrev).map(|&id| id);
    }

    pub fn find_all_subdirs(&self, tree: &Tree) -> Vec<String>
    {
        let mut sd = vec![];
        for item in tree {
            if let Ok(st) = self.repo.find_tree(item.id()) {
                let name = item.name().unwrap();
                if !name.starts_with(".") {
                    sd.push(name.to_string());
                    for r in self.find_all_subdirs(&st) {
                        sd.push(format!("{}/{}", name, r));
                    }
                }
            }
        }
        return sd;
    }
}
<|endoftext|>


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

//! Generic API bits for implementing new services.

use std::marker::PhantomData;

use hyper::Url;
use hyper::client::{Body, RequestBuilder as HyperRequestBuilder, Response};
use hyper::header::{Header, HeaderFormat, Headers};
pub use hyper::method::Method;
use serde::{Deserialize, Serialize};
use serde_json;

use super::{ApiError, ApiResult, ApiVersion, ApiVersionRequest, Session};
use super::auth::Method as AuthMethod;
use super::identity::protocol::AuthTokenHeader;
use super::utils;

/// Request builder with authentication, error checking and JSON support.
///
/// Partly copies the interface of hyper::client::RequestBuilder.
#[allow(missing_debug_implementations)]
pub struct RequestBuilder<'a, A: AuthMethod + 'a> {
    parent: &'a Session<A>,
    inner: HyperRequestBuilder<'a>
}

/// Type of query parameters.
#[derive(Clone, Debug)]
pub struct Query(pub Vec<(String, String)>);

/// Information about API endpoint.
#[derive(Clone, Debug)]
pub struct ServiceInfo {
    /// Root endpoint.
    pub root_url: Url,
    /// Current API version (if supported).
    pub current_version: Option<ApiVersion>,
    /// Minimum API version (if supported).
    pub minimum_version: Option<ApiVersion>
}

/// Trait representing a service type.
pub trait ServiceType {
    /// Service type to pass to the catalog.
    fn catalog_type() -> &'static str;

    /// Get basic service information.
    fn service_info<Auth: AuthMethod>(endpoint: Url, session: &Session<Auth>)
        -> ApiResult<ServiceInfo>;
}

/// Trait representing a service with API version support.
pub trait ApiVersioning {
    /// Return headers to set for this API version.
    fn api_version_headers(version: ApiVersion) -> ApiResult<Headers>;
}

/// A service-specific wrapper around Session.
#[derive(Debug)]
pub struct ServiceWrapper<'session, Auth: AuthMethod + 'session,
                          Srv: ServiceType> {
    session: &'session Session<Auth>,
    service_type: PhantomData<Srv>
}

impl Query {
    /// Empty query.
    pub fn new() -> Query {
        Query(Vec::new())
    }

    /// Add an item to the query.
    pub fn push<K, V>(&mut self, param: K, value: V)
            where K: Into<String>, V: ToString {
        self.0.push((param.into(), value.to_string()))
    }

    /// Add a strng item to the query.
    pub fn push_str<K, V>(&mut self, param: K, value: V)
            where K: Into<String>, V: Into<String> {
        self.0.push((param.into(), value.into()))
    }
}

impl<'a, Auth: AuthMethod> RequestBuilder<'a, Auth> {
    /// Wrap a request builder.
    pub fn new(inner: HyperRequestBuilder<'a>, session: &'a Session<Auth>)
            -> RequestBuilder<'a, Auth> {
        RequestBuilder {
            parent: session,
            inner: inner
        }
    }

    /// Send this request.
    pub fn send(self) -> ApiResult<Response> {
        let resp = try!(self.send_unchecked());
        if resp.status.is_success() {
            Ok(resp)
        } else {
            Err(ApiError::HttpError(resp.status, resp))
        }
    }

    /// Send this request without checking on status code.
    pub fn send_unchecked(self) -> ApiResult<Response> {
        let token = try!(self.parent.auth_token());
        let hdr = AuthTokenHeader(token.into());
        self.inner.header(hdr).send().map_err(From::from)
    }

    /// Send this request and parse JSON response on success.
    pub fn fetch_json<T: Deserialize>(self) -> ApiResult<T> {
        let resp = try!(self.send());
        serde_json::from_reader(resp).map_err(From::from)
    }

    /// Add body to the request.
    pub fn body<B: Into<Body<'a>>>(self, body: B)
            -> RequestBuilder<'a, Auth> {
        RequestBuilder {
            inner: self.inner.body(body),
            .. self
        }
    }

    /// Add additional headers to the request.
    pub fn headers(self, headers: Headers)
            -> RequestBuilder<'a, Auth> {
        RequestBuilder {
            inner: self.inner.headers(headers),
            .. self
        }
    }

    /// Add an individual header to the request.
    ///
    /// Note that X-Auth-Token is always overwritten with a token in use.
    pub fn header<H: Header + HeaderFormat>(self, header: H)
            -> RequestBuilder<'a, Auth> {
        RequestBuilder {
            inner: self.inner.header(header),
            .. self
        }
    }
}

impl<'session, Auth: AuthMethod + 'session, Srv: ServiceType>
        ServiceWrapper<'session, Auth, Srv> {
    /// Create a new wrapper for the specific service.
    pub fn new(session: &'session Session<Auth>)
            -> ServiceWrapper<'session, Auth, Srv> {
        ServiceWrapper {
            session: session,
            service_type: PhantomData
        }
    }

    /// Construct and endpoint for the given service from the path.
    pub fn get_endpoint<P>(&self, path: P, query: Query) -> ApiResult<Url>
            where P: IntoIterator, P::Item: AsRef<str> {
        let info = try!(self.session.get_service_info::<Srv>(None));
        let mut url = utils::url::extend(info.root_url, path);
        let _ = url.query_pairs_mut().extend_pairs(query.0);
        Ok(url)
    }

    /// Make an HTTP request to the given service.
    pub fn request<P>(&'session self, method: Method, path: P, query: Query)
            -> ApiResult<RequestBuilder<'session, Auth>>
            where P: IntoIterator, P::Item: AsRef<str> {
        let url = try!(self.get_endpoint(path, query));
        let headers = self.session.service_headers::<Srv>();
        trace!("Sending HTTP {} request to {} with {:?}",
               method, url, headers);
        Ok(self.session.raw_request(method, url).headers(headers))
    }

    /// Make an HTTP request with JSON body and JSON response.
    pub fn json<P, Req, Res>(&self, method: Method, path: P, query: Query,
                             body: &Req) -> ApiResult<Res>
            where Req: Serialize, Res: Deserialize,
            P: IntoIterator, P::Item: AsRef<str> {
        let str_body = try!(serde_json::to_string(body));
        let request = try!(self.request(method, path, query));
        request.body(&str_body).fetch_json()
    }

    /// Make a GET request returning a JSON.
    pub fn get_json<P, Res>(&self, path: P, query: Query) -> ApiResult<Res>
            where Res: Deserialize, P: IntoIterator, P::Item: AsRef<str> {
        try!(self.request(Method::Get, path, query)).fetch_json()
    }

    /// Make a POST request sending and returning a JSON.
    pub fn post_json<P, Req, Res>(&self, path: P, query: Query, body: &Req)
            -> ApiResult<Res> where Req: Serialize, Res: Deserialize,
            P: IntoIterator, P::Item: AsRef<str> {
        self.json(Method::Post, path, query, body)
    }

    /// Make a POST request sending and returning a JSON.
    pub fn put_json<P, Req, Res>(&self, path: P, query: Query, body: &Req)
            -> ApiResult<Res> where Req: Serialize, Res: Deserialize,
            P: IntoIterator, P::Item: AsRef<str> {
        self.json(Method::Put, path, query, body)
    }

    /// Make a PATCH request sending and returning a JSON.
    pub fn patch_json<P, Req, Res>(&self, path: P, query: Query, body: &Req)
            -> ApiResult<Res> where Req: Serialize, Res: Deserialize,
            P: IntoIterator, P::Item: AsRef<str> {
        self.json(Method::Patch, path, query, body)
    }

    /// Make a DELETE request.
    pub fn delete<P>(&self, path: P, query: Query) -> ApiResult<Response>
            where P: IntoIterator, P::Item: AsRef<str> {
        try!(self.request(Method::Delete, path, query)).send()
    }
}

impl<'session, Auth: AuthMethod + 'session, Srv: ServiceType>
        Clone for ServiceWrapper<'session, Auth, Srv> {
    fn clone(&self) -> ServiceWrapper<'session, Auth, Srv> {
        ServiceWrapper {
            session: self.session,
            service_type: PhantomData
        }
    }
}

impl ServiceInfo {
    /// Pick an API version.
    pub fn pick_api_version(&self, request: ApiVersionRequest)
            -> Option<ApiVersion> {
        match request {
            ApiVersionRequest::Minimum =>
                self.minimum_version,
            ApiVersionRequest::Latest =>
                self.current_version,
            ApiVersionRequest::Exact(req) => {
                self.current_version.and_then(|max| {
                    match self.minimum_version {
                        Some(min) if req >= min && req <= max => Some(req),
                        None if req == max => Some(req),
                        _ => None
                    }
                })
            },
            ApiVersionRequest::Choice(vec) => {
                if vec.is_empty() {
                    return None;
                }

                self.current_version.and_then(|max| {
                    match self.minimum_version {
                        Some(min) => vec.into_iter().filter(|x| {
                            *x >= min && *x <= max
                        }).max(),
                        None =>vec.into_iter().find(|x| *x == max)
                    }
                })
            }
        }
    }
}

#[cfg(test)]
pub mod test {
    use hyper::Url;

    use super::super::{ApiVersion, ApiVersionRequest};
    use super::ServiceInfo;

    fn service_info(min: Option<u16>, max: Option<u16>) -> ServiceInfo {
        ServiceInfo {
            root_url: Url::parse("http://127.0.0.1").unwrap(),
            minimum_version: min.map(|x| ApiVersion(2, x)),
            current_version: max.map(|x| ApiVersion(2, x)),
        }
    }

    #[test]
    fn test_pick_version_exact() {
        let info = service_info(Some(1), Some(24));
        let version = ApiVersion(2, 22);
        let result = info.pick_api_version(ApiVersionRequest::Exact(version))
            .unwrap();
        assert_eq!(result, version);
    }

    #[test]
    fn test_pick_version_exact_mismatch() {
        let info = service_info(Some(1), Some(24));
        let version = ApiVersion(2, 25);
        let res1 = info.pick_api_version(ApiVersionRequest::Exact(version));
        assert!(res1.is_none());
        let version2 = ApiVersion(1, 11);
        let res2 = info.pick_api_version(ApiVersionRequest::Exact(version2));
        assert!(res2.is_none());
    }

    #[test]
    fn test_pick_version_exact_current_only() {
        let info = service_info(None, Some(24));
        let version = ApiVersion(2, 24);
        let result = info.pick_api_version(ApiVersionRequest::Exact(version))
            .unwrap();
        assert_eq!(result, version);
    }

    #[test]
    fn test_pick_version_exact_current_only_mismatch() {
        let info = service_info(None, Some(24));
        let version = ApiVersion(2, 22);
        let result = info.pick_api_version(ApiVersionRequest::Exact(version));
        assert!(result.is_none());
    }

    #[test]
    fn test_pick_version_minimum() {
        let info = service_info(Some(1), Some(24));
        let result = info.pick_api_version(ApiVersionRequest::Minimum)
            .unwrap();
        assert_eq!(result, ApiVersion(2, 1));
    }

    #[test]
    fn test_pick_version_minimum_unknown() {
        let info = service_info(None, Some(24));
        let result = info.pick_api_version(ApiVersionRequest::Minimum);
        assert!(result.is_none());
    }

    #[test]
    fn test_pick_version_latest() {
        let info = service_info(Some(1), Some(24));
        let result = info.pick_api_version(ApiVersionRequest::Latest)
            .unwrap();
        assert_eq!(result, ApiVersion(2, 24));
    }

    #[test]
    fn test_pick_version_latest_unknown() {
        let info = service_info(Some(1), None);
        let result = info.pick_api_version(ApiVersionRequest::Latest);
        assert!(result.is_none());
    }

    #[test]
    fn test_pick_version_choice() {
        let info = service_info(Some(1), Some(24));
        let choice = vec![ApiVersion(2, 0), ApiVersion(2, 2),
                          ApiVersion(2, 22), ApiVersion(2, 25)];
        let result = info.pick_api_version(ApiVersionRequest::Choice(choice))
            .unwrap();
        assert_eq!(result, ApiVersion(2, 22));
    }

    #[test]
    fn test_pick_version_choice_mismatch() {
        let info = service_info(Some(1), Some(24));
        let choice = vec![ApiVersion(2, 0), ApiVersion(2, 25)];
        let result = info.pick_api_version(ApiVersionRequest::Choice(choice));
        assert!(result.is_none());
    }

    #[test]
    fn test_pick_version_choice_current_only() {
        let info = service_info(None, Some(24));
        let choice = vec![ApiVersion(2, 0), ApiVersion(2, 2),
                          ApiVersion(2, 24), ApiVersion(2, 25)];
        let result = info.pick_api_version(ApiVersionRequest::Choice(choice))
            .unwrap();
        assert_eq!(result, ApiVersion(2, 24));
    }

    #[test]
    fn test_pick_version_choice_current_only_mismatch() {
        let info = service_info(None, Some(24));
        let choice = vec![ApiVersion(2, 0), ApiVersion(2, 2),
                          ApiVersion(2, 22), ApiVersion(2, 25)];
        let result = info.pick_api_version(ApiVersionRequest::Choice(choice));
        assert!(result.is_none());
    }
}
<commit_msg>feat(service): allow changing endpoint interface for ServiceWrapper<commit_after>// Copyright 2017 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Generic API bits for implementing new services.

use std::marker::PhantomData;

use hyper::Url;
use hyper::client::{Body, RequestBuilder as HyperRequestBuilder, Response};
use hyper::header::{Header, HeaderFormat, Headers};
pub use hyper::method::Method;
use serde::{Deserialize, Serialize};
use serde_json;

use super::{ApiError, ApiResult, ApiVersion, ApiVersionRequest, Session};
use super::auth::Method as AuthMethod;
use super::identity::protocol::AuthTokenHeader;
use super::utils;

/// Request builder with authentication, error checking and JSON support.
///
/// Partly copies the interface of hyper::client::RequestBuilder.
#[allow(missing_debug_implementations)]
pub struct RequestBuilder<'a, A: AuthMethod + 'a> {
    parent: &'a Session<A>,
    inner: HyperRequestBuilder<'a>
}

/// Type of query parameters.
#[derive(Clone, Debug)]
pub struct Query(pub Vec<(String, String)>);

/// Information about API endpoint.
#[derive(Clone, Debug)]
pub struct ServiceInfo {
    /// Root endpoint.
    pub root_url: Url,
    /// Current API version (if supported).
    pub current_version: Option<ApiVersion>,
    /// Minimum API version (if supported).
    pub minimum_version: Option<ApiVersion>
}

/// Trait representing a service type.
pub trait ServiceType {
    /// Service type to pass to the catalog.
    fn catalog_type() -> &'static str;

    /// Get basic service information.
    fn service_info<Auth: AuthMethod>(endpoint: Url, session: &Session<Auth>)
        -> ApiResult<ServiceInfo>;
}

/// Trait representing a service with API version support.
pub trait ApiVersioning {
    /// Return headers to set for this API version.
    fn api_version_headers(version: ApiVersion) -> ApiResult<Headers>;
}

/// A service-specific wrapper around Session.
#[derive(Debug)]
pub struct ServiceWrapper<'session, Auth: AuthMethod + 'session,
                          Srv: ServiceType> {
    session: &'session Session<Auth>,
    service_type: PhantomData<Srv>,
    endpoint_interface: Option<String>
}

impl Query {
    /// Empty query.
    pub fn new() -> Query {
        Query(Vec::new())
    }

    /// Add an item to the query.
    pub fn push<K, V>(&mut self, param: K, value: V)
            where K: Into<String>, V: ToString {
        self.0.push((param.into(), value.to_string()))
    }

    /// Add a strng item to the query.
    pub fn push_str<K, V>(&mut self, param: K, value: V)
            where K: Into<String>, V: Into<String> {
        self.0.push((param.into(), value.into()))
    }
}

impl<'a, Auth: AuthMethod> RequestBuilder<'a, Auth> {
    /// Wrap a request builder.
    pub fn new(inner: HyperRequestBuilder<'a>, session: &'a Session<Auth>)
            -> RequestBuilder<'a, Auth> {
        RequestBuilder {
            parent: session,
            inner: inner
        }
    }

    /// Send this request.
    pub fn send(self) -> ApiResult<Response> {
        let resp = try!(self.send_unchecked());
        if resp.status.is_success() {
            Ok(resp)
        } else {
            Err(ApiError::HttpError(resp.status, resp))
        }
    }

    /// Send this request without checking on status code.
    pub fn send_unchecked(self) -> ApiResult<Response> {
        let token = try!(self.parent.auth_token());
        let hdr = AuthTokenHeader(token.into());
        self.inner.header(hdr).send().map_err(From::from)
    }

    /// Send this request and parse JSON response on success.
    pub fn fetch_json<T: Deserialize>(self) -> ApiResult<T> {
        let resp = try!(self.send());
        serde_json::from_reader(resp).map_err(From::from)
    }

    /// Add body to the request.
    pub fn body<B: Into<Body<'a>>>(self, body: B)
            -> RequestBuilder<'a, Auth> {
        RequestBuilder {
            inner: self.inner.body(body),
            .. self
        }
    }

    /// Add additional headers to the request.
    pub fn headers(self, headers: Headers)
            -> RequestBuilder<'a, Auth> {
        RequestBuilder {
            inner: self.inner.headers(headers),
            .. self
        }
    }

    /// Add an individual header to the request.
    ///
    /// Note that X-Auth-Token is always overwritten with a token in use.
    pub fn header<H: Header + HeaderFormat>(self, header: H)
            -> RequestBuilder<'a, Auth> {
        RequestBuilder {
            inner: self.inner.header(header),
            .. self
        }
    }
}

impl<'session, Auth: AuthMethod + 'session, Srv: ServiceType>
        ServiceWrapper<'session, Auth, Srv> {
    /// Create a new wrapper for the specific service.
    pub fn new(session: &'session Session<Auth>)
            -> ServiceWrapper<'session, Auth, Srv> {
        ServiceWrapper {
            session: session,
            service_type: PhantomData,
            endpoint_interface: None
        }
    }

    /// Change the endpoint interface used for this wrapper.
    pub fn with_endpoint_interface(self, endpoint_interface: String)
            -> ServiceWrapper<'session, Auth, Srv> {
        ServiceWrapper {
            endpoint_interface: Some(endpoint_interface),
            .. self
        }
    }

    /// Construct and endpoint for the given service from the path.
    pub fn get_endpoint<P>(&self, path: P, query: Query) -> ApiResult<Url>
            where P: IntoIterator, P::Item: AsRef<str> {
        let ep = self.endpoint_interface.clone();
        let info = try!(self.session.get_service_info::<Srv>(ep));
        let mut url = utils::url::extend(info.root_url, path);
        let _ = url.query_pairs_mut().extend_pairs(query.0);
        Ok(url)
    }

    /// Make an HTTP request to the given service.
    pub fn request<P>(&'session self, method: Method, path: P, query: Query)
            -> ApiResult<RequestBuilder<'session, Auth>>
            where P: IntoIterator, P::Item: AsRef<str> {
        let url = try!(self.get_endpoint(path, query));
        let headers = self.session.service_headers::<Srv>();
        trace!("Sending HTTP {} request to {} with {:?}",
               method, url, headers);
        Ok(self.session.raw_request(method, url).headers(headers))
    }

    /// Make an HTTP request with JSON body and JSON response.
    pub fn json<P, Req, Res>(&self, method: Method, path: P, query: Query,
                             body: &Req) -> ApiResult<Res>
            where Req: Serialize, Res: Deserialize,
            P: IntoIterator, P::Item: AsRef<str> {
        let str_body = try!(serde_json::to_string(body));
        let request = try!(self.request(method, path, query));
        request.body(&str_body).fetch_json()
    }

    /// Make a GET request returning a JSON.
    pub fn get_json<P, Res>(&self, path: P, query: Query) -> ApiResult<Res>
            where Res: Deserialize, P: IntoIterator, P::Item: AsRef<str> {
        try!(self.request(Method::Get, path, query)).fetch_json()
    }

    /// Make a POST request sending and returning a JSON.
    pub fn post_json<P, Req, Res>(&self, path: P, query: Query, body: &Req)
            -> ApiResult<Res> where Req: Serialize, Res: Deserialize,
            P: IntoIterator, P::Item: AsRef<str> {
        self.json(Method::Post, path, query, body)
    }

    /// Make a POST request sending and returning a JSON.
    pub fn put_json<P, Req, Res>(&self, path: P, query: Query, body: &Req)
            -> ApiResult<Res> where Req: Serialize, Res: Deserialize,
            P: IntoIterator, P::Item: AsRef<str> {
        self.json(Method::Put, path, query, body)
    }

    /// Make a PATCH request sending and returning a JSON.
    pub fn patch_json<P, Req, Res>(&self, path: P, query: Query, body: &Req)
            -> ApiResult<Res> where Req: Serialize, Res: Deserialize,
            P: IntoIterator, P::Item: AsRef<str> {
        self.json(Method::Patch, path, query, body)
    }

    /// Make a DELETE request.
    pub fn delete<P>(&self, path: P, query: Query) -> ApiResult<Response>
            where P: IntoIterator, P::Item: AsRef<str> {
        try!(self.request(Method::Delete, path, query)).send()
    }
}

impl<'session, Auth: AuthMethod + 'session, Srv: ServiceType>
        Clone for ServiceWrapper<'session, Auth, Srv> {
    fn clone(&self) -> ServiceWrapper<'session, Auth, Srv> {
        ServiceWrapper {
            session: self.session,
            service_type: PhantomData,
            endpoint_interface: self.endpoint_interface.clone()
        }
    }
}

impl ServiceInfo {
    /// Pick an API version.
    pub fn pick_api_version(&self, request: ApiVersionRequest)
            -> Option<ApiVersion> {
        match request {
            ApiVersionRequest::Minimum =>
                self.minimum_version,
            ApiVersionRequest::Latest =>
                self.current_version,
            ApiVersionRequest::Exact(req) => {
                self.current_version.and_then(|max| {
                    match self.minimum_version {
                        Some(min) if req >= min && req <= max => Some(req),
                        None if req == max => Some(req),
                        _ => None
                    }
                })
            },
            ApiVersionRequest::Choice(vec) => {
                if vec.is_empty() {
                    return None;
                }

                self.current_version.and_then(|max| {
                    match self.minimum_version {
                        Some(min) => vec.into_iter().filter(|x| {
                            *x >= min && *x <= max
                        }).max(),
                        None =>vec.into_iter().find(|x| *x == max)
                    }
                })
            }
        }
    }
}

#[cfg(test)]
pub mod test {
    use hyper::Url;

    use super::super::{ApiVersion, ApiVersionRequest};
    use super::ServiceInfo;

    fn service_info(min: Option<u16>, max: Option<u16>) -> ServiceInfo {
        ServiceInfo {
            root_url: Url::parse("http://127.0.0.1").unwrap(),
            minimum_version: min.map(|x| ApiVersion(2, x)),
            current_version: max.map(|x| ApiVersion(2, x)),
        }
    }

    #[test]
    fn test_pick_version_exact() {
        let info = service_info(Some(1), Some(24));
        let version = ApiVersion(2, 22);
        let result = info.pick_api_version(ApiVersionRequest::Exact(version))
            .unwrap();
        assert_eq!(result, version);
    }

    #[test]
    fn test_pick_version_exact_mismatch() {
        let info = service_info(Some(1), Some(24));
        let version = ApiVersion(2, 25);
        let res1 = info.pick_api_version(ApiVersionRequest::Exact(version));
        assert!(res1.is_none());
        let version2 = ApiVersion(1, 11);
        let res2 = info.pick_api_version(ApiVersionRequest::Exact(version2));
        assert!(res2.is_none());
    }

    #[test]
    fn test_pick_version_exact_current_only() {
        let info = service_info(None, Some(24));
        let version = ApiVersion(2, 24);
        let result = info.pick_api_version(ApiVersionRequest::Exact(version))
            .unwrap();
        assert_eq!(result, version);
    }

    #[test]
    fn test_pick_version_exact_current_only_mismatch() {
        let info = service_info(None, Some(24));
        let version = ApiVersion(2, 22);
        let result = info.pick_api_version(ApiVersionRequest::Exact(version));
        assert!(result.is_none());
    }

    #[test]
    fn test_pick_version_minimum() {
        let info = service_info(Some(1), Some(24));
        let result = info.pick_api_version(ApiVersionRequest::Minimum)
            .unwrap();
        assert_eq!(result, ApiVersion(2, 1));
    }

    #[test]
    fn test_pick_version_minimum_unknown() {
        let info = service_info(None, Some(24));
        let result = info.pick_api_version(ApiVersionRequest::Minimum);
        assert!(result.is_none());
    }

    #[test]
    fn test_pick_version_latest() {
        let info = service_info(Some(1), Some(24));
        let result = info.pick_api_version(ApiVersionRequest::Latest)
            .unwrap();
        assert_eq!(result, ApiVersion(2, 24));
    }

    #[test]
    fn test_pick_version_latest_unknown() {
        let info = service_info(Some(1), None);
        let result = info.pick_api_version(ApiVersionRequest::Latest);
        assert!(result.is_none());
    }

    #[test]
    fn test_pick_version_choice() {
        let info = service_info(Some(1), Some(24));
        let choice = vec![ApiVersion(2, 0), ApiVersion(2, 2),
                          ApiVersion(2, 22), ApiVersion(2, 25)];
        let result = info.pick_api_version(ApiVersionRequest::Choice(choice))
            .unwrap();
        assert_eq!(result, ApiVersion(2, 22));
    }

    #[test]
    fn test_pick_version_choice_mismatch() {
        let info = service_info(Some(1), Some(24));
        let choice = vec![ApiVersion(2, 0), ApiVersion(2, 25)];
        let result = info.pick_api_version(ApiVersionRequest::Choice(choice));
        assert!(result.is_none());
    }

    #[test]
    fn test_pick_version_choice_current_only() {
        let info = service_info(None, Some(24));
        let choice = vec![ApiVersion(2, 0), ApiVersion(2, 2),
                          ApiVersion(2, 24), ApiVersion(2, 25)];
        let result = info.pick_api_version(ApiVersionRequest::Choice(choice))
            .unwrap();
        assert_eq!(result, ApiVersion(2, 24));
    }

    #[test]
    fn test_pick_version_choice_current_only_mismatch() {
        let info = service_info(None, Some(24));
        let choice = vec![ApiVersion(2, 0), ApiVersion(2, 2),
                          ApiVersion(2, 22), ApiVersion(2, 25)];
        let result = info.pick_api_version(ApiVersionRequest::Choice(choice));
        assert!(result.is_none());
    }
}
<|endoftext|>


<commit_before>use base64::{decode, encode};
use std::collections::HashMap;
use std::error::Error;
use std::str;

use conduit::{Request, Response};
use conduit_middleware;
use cookie::{Cookie, Key};

use super::RequestCookies;

pub struct SessionMiddleware {
    cookie_name: String,
    key: Key,
    secure: bool,
}

pub struct Session {
    pub data: HashMap<String, String>,
}

impl SessionMiddleware {
    pub fn new(cookie: &str, key: Key, secure: bool) -> SessionMiddleware {
        SessionMiddleware {
            cookie_name: cookie.to_string(),
            key: key,
            secure: secure,
        }
    }

    pub fn decode(&self, cookie: Cookie<'_>) -> HashMap<String, String> {
        let mut ret = HashMap::new();
        let bytes = decode(cookie.value().as_bytes()).unwrap_or_default();
        let mut parts = bytes.split(|&a| a == 0xff);
        while let (Some(key), Some(value)) = (parts.next(), parts.next()) {
            if key.is_empty() {
                break;
            }
            if let (Ok(key), Ok(value)) = (str::from_utf8(key), str::from_utf8(value)) {
                ret.insert(key.to_string(), value.to_string());
            }
        }
        ret
    }

    pub fn encode(&self, h: &HashMap<String, String>) -> String {
        let mut ret = Vec::new();
        for (i, (k, v)) in h.iter().enumerate() {
            if i != 0 {
                ret.push(0xff)
            }
            ret.extend(k.bytes());
            ret.push(0xff);
            ret.extend(v.bytes());
        }
        while ret.len() * 8 % 6 != 0 {
            ret.push(0xff);
        }
        encode(&ret[..])
    }
}

impl conduit_middleware::Middleware for SessionMiddleware {
    fn before(&self, req: &mut dyn Request) -> Result<(), Box<dyn Error + Send>> {
        let session = {
            let jar = req.cookies_mut().signed(&self.key);
            jar.get(&self.cookie_name)
                .map(|cookie| self.decode(cookie))
                .unwrap_or_else(HashMap::new)
        };
        req.mut_extensions().insert(Session { data: session });
        Ok(())
    }

    fn after(
        &self,
        req: &mut dyn Request,
        res: Result<Response, Box<dyn Error + Send>>,
    ) -> Result<Response, Box<dyn Error + Send>> {
        let cookie = {
            let session = req.mut_extensions().find::<Session>();
            let session = session.expect("session must be present after request");
            let encoded = self.encode(&session.data);
            Cookie::build(self.cookie_name.to_string(), encoded)
                .http_only(true)
                .secure(self.secure)
                .path("/")
                .finish()
        };
        req.cookies_mut().signed(&self.key).add(cookie);
        res
    }
}

pub trait RequestSession {
    fn session(&mut self) -> &mut HashMap<String, String>;
}

impl<T: Request + ?Sized> RequestSession for T {
    fn session(&mut self) -> &mut HashMap<String, String> {
        &mut self
            .mut_extensions()
            .find_mut::<Session>()
            .expect("missing cookie session")
            .data
    }
}

#[cfg(test)]
mod test {

    use std::collections::HashMap;
    use std::io::{self, Cursor};

    use conduit::{Handler, Method, Request, Response};
    use conduit_middleware::MiddlewareBuilder;
    use conduit_test::MockRequest;
    use cookie::{Cookie, Key};

    use {Middleware, RequestSession, SessionMiddleware};

    fn test_key() -> Key {
        let master_key: Vec<u8> = (0..32).collect();
        Key::from_master(&master_key)
    }

    #[test]
    fn simple() {
        let mut req = MockRequest::new(Method::Post, "/articles");
        let key = test_key();

        // Set the session cookie
        let mut app = MiddlewareBuilder::new(set_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        let response = app.call(&mut req).ok().unwrap();

        let v = &response.headers["Set-Cookie"];
        assert!(v[0].starts_with("lol"));

        // Use the session cookie
        req.header("Cookie", &v[0]);
        let key = test_key();
        let mut app = MiddlewareBuilder::new(use_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        assert!(app.call(&mut req).is_ok());

        fn set_session(req: &mut dyn Request) -> io::Result<Response> {
            assert!(req
                .session()
                .insert("foo".to_string(), "bar".to_string())
                .is_none());
            Ok(Response {
                status: (200, "OK"),
                headers: HashMap::new(),
                body: Box::new(Cursor::new(Vec::new())),
            })
        }
        fn use_session(req: &mut dyn Request) -> io::Result<Response> {
            assert_eq!(*req.session().get("foo").unwrap(), "bar");
            Ok(Response {
                status: (200, "OK"),
                headers: HashMap::new(),
                body: Box::new(Cursor::new(Vec::new())),
            })
        }
    }

    #[test]
    fn no_equals() {
        let key = test_key();
        let m = SessionMiddleware::new("test", key, false);
        let e = {
            let mut map = HashMap::new();
            map.insert("a".to_string(), "bc".to_string());
            m.encode(&map)
        };
        assert!(!e.ends_with("="));
        let m = m.decode(Cookie::new("foo".to_string(), e));
        assert_eq!(*m.get("a").unwrap(), "bc");
    }
}
<commit_msg>Minor cleanups<commit_after>use base64::{decode, encode};
use std::collections::HashMap;
use std::error::Error;
use std::str;

use conduit::{Request, Response};
use conduit_middleware;
use cookie::{Cookie, Key};

use super::RequestCookies;

pub struct SessionMiddleware {
    cookie_name: String,
    key: Key,
    secure: bool,
}

pub struct Session {
    pub data: HashMap<String, String>,
}

impl SessionMiddleware {
    pub fn new(cookie: &str, key: Key, secure: bool) -> SessionMiddleware {
        SessionMiddleware {
            cookie_name: cookie.to_string(),
            key,
            secure,
        }
    }

    pub fn decode(&self, cookie: Cookie<'_>) -> HashMap<String, String> {
        let mut ret = HashMap::new();
        let bytes = decode(cookie.value().as_bytes()).unwrap_or_default();
        let mut parts = bytes.split(|&a| a == 0xff);
        while let (Some(key), Some(value)) = (parts.next(), parts.next()) {
            if key.is_empty() {
                break;
            }
            if let (Ok(key), Ok(value)) = (str::from_utf8(key), str::from_utf8(value)) {
                ret.insert(key.to_string(), value.to_string());
            }
        }
        ret
    }

    pub fn encode(&self, h: &HashMap<String, String>) -> String {
        let mut ret = Vec::new();
        for (i, (k, v)) in h.iter().enumerate() {
            if i != 0 {
                ret.push(0xff)
            }
            ret.extend(k.bytes());
            ret.push(0xff);
            ret.extend(v.bytes());
        }
        while ret.len() * 8 % 6 != 0 {
            ret.push(0xff);
        }
        encode(&ret[..])
    }
}

impl conduit_middleware::Middleware for SessionMiddleware {
    fn before(&self, req: &mut dyn Request) -> Result<(), Box<dyn Error + Send>> {
        let session = {
            let jar = req.cookies_mut().signed(&self.key);
            jar.get(&self.cookie_name)
                .map(|cookie| self.decode(cookie))
                .unwrap_or_else(HashMap::new)
        };
        req.mut_extensions().insert(Session { data: session });
        Ok(())
    }

    fn after(
        &self,
        req: &mut dyn Request,
        res: Result<Response, Box<dyn Error + Send>>,
    ) -> Result<Response, Box<dyn Error + Send>> {
        let cookie = {
            let session = req.mut_extensions().find::<Session>();
            let session = session.expect("session must be present after request");
            let encoded = self.encode(&session.data);
            Cookie::build(self.cookie_name.to_string(), encoded)
                .http_only(true)
                .secure(self.secure)
                .path("/")
                .finish()
        };
        req.cookies_mut().signed(&self.key).add(cookie);
        res
    }
}

pub trait RequestSession {
    fn session(&mut self) -> &mut HashMap<String, String>;
}

impl<T: Request + ?Sized> RequestSession for T {
    fn session(&mut self) -> &mut HashMap<String, String> {
        &mut self
            .mut_extensions()
            .find_mut::<Session>()
            .expect("missing cookie session")
            .data
    }
}

#[cfg(test)]
mod test {

    use std::collections::HashMap;
    use std::io::{self, Cursor};

    use conduit::{Handler, Method, Request, Response};
    use conduit_middleware::MiddlewareBuilder;
    use conduit_test::MockRequest;
    use cookie::{Cookie, Key};

    use {Middleware, RequestSession, SessionMiddleware};

    fn test_key() -> Key {
        let master_key: Vec<u8> = (0..32).collect();
        Key::from_master(&master_key)
    }

    #[test]
    fn simple() {
        let mut req = MockRequest::new(Method::Post, "/articles");
        let key = test_key();

        // Set the session cookie
        let mut app = MiddlewareBuilder::new(set_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        let response = app.call(&mut req).ok().unwrap();

        let v = &response.headers["Set-Cookie"];
        assert!(v[0].starts_with("lol"));

        // Use the session cookie
        req.header("Cookie", &v[0]);
        let key = test_key();
        let mut app = MiddlewareBuilder::new(use_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        assert!(app.call(&mut req).is_ok());

        fn set_session(req: &mut dyn Request) -> io::Result<Response> {
            assert!(req
                .session()
                .insert("foo".to_string(), "bar".to_string())
                .is_none());
            Ok(Response {
                status: (200, "OK"),
                headers: HashMap::new(),
                body: Box::new(Cursor::new(Vec::new())),
            })
        }
        fn use_session(req: &mut dyn Request) -> io::Result<Response> {
            assert_eq!(*req.session().get("foo").unwrap(), "bar");
            Ok(Response {
                status: (200, "OK"),
                headers: HashMap::new(),
                body: Box::new(Cursor::new(Vec::new())),
            })
        }
    }

    #[test]
    fn no_equals() {
        let key = test_key();
        let m = SessionMiddleware::new("test", key, false);
        let e = {
            let mut map = HashMap::new();
            map.insert("a".to_string(), "bc".to_string());
            m.encode(&map)
        };
        assert!(!e.ends_with("="));
        let m = m.decode(Cookie::new("foo".to_string(), e));
        assert_eq!(*m.get("a").unwrap(), "bc");
    }
}
<|endoftext|>


<commit_before>use gfx;
use image;
use image::{
    GenericImage,
    ImageBuf,
    Rgba,
};
use texture_lib::{ FromMemoryAlpha, ImageSize };

/// Represents a texture.
pub struct Texture {
    /// A handle to the Gfx texture.
    pub handle: gfx::TextureHandle,
}

impl Texture {
    /// Creates a texture from path.
    pub fn from_path<
        C: gfx::CommandBuffer,
        D: gfx::Device<C>
    >(device: &mut D, path: &Path) -> Result<Texture, String> {
        let img = match image::open(path) {
            Ok(img) => img,
            Err(e)  => return Err(format!("Could not load '{}': {}",
                path.filename_str().unwrap(), e)),
        };

        match img.color() {
            image::RGBA(8) => {},
            c => return Err(format!("Unsupported color type {}", c)),
        };

        let (width, height) = img.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).unwrap();
        device.update_texture(&texture, &image_info,
            img.raw_pixels().as_slice())
        .unwrap();

        Ok(Texture {
            handle: texture
        })
    }

    /// Creates a texture from image.
    pub fn from_image<
        C: gfx::CommandBuffer,
        D: gfx::Device<C>
    >(device: &mut D, image: &ImageBuf<Rgba<u8>>) -> Texture {
        let (width, height) = image.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).unwrap();
        device.update_texture(&texture, &image_info,
            image.pixelbuf().as_slice())
        .unwrap();

        Texture {
            handle: texture
        }
    }

    /// Creates a texture from RGBA image.
    pub fn from_rgba8<D: gfx::Device<C>, C: gfx::CommandBuffer>(
        img: ImageBuf<Rgba<u8>>,
        d: &mut D
    ) -> Texture {
        let (width, height) = img.dimensions();

        let mut ti = gfx::tex::TextureInfo::new();
        ti.width = width as u16;
        ti.height = height as u16;
        ti.kind = gfx::tex::Texture2D;
        ti.format = gfx::tex::RGBA8;

        let tex = d.create_texture(ti).unwrap();
        d.update_texture(&tex, &ti.to_image_info(),
                         img.into_vec().as_slice()).unwrap();
        d.generate_mipmap(&tex);

        Texture {
            handle: tex,
        }
    }

    /// Updates the texture with an image.
    pub fn update<
        C: gfx::CommandBuffer,
        D: gfx::Device<C>
    >(&mut self, device: &mut D, image: &ImageBuf<Rgba<u8>>) {
        device.update_texture(&self.handle,
            &self.handle.get_info().to_image_info(),
            image.pixelbuf().as_slice()
        ).unwrap();
    }

    /// Gets the size of the texture.
    #[inline(always)]
    pub fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }

    /// Gets the width of the texture.
    #[inline(always)]
    pub fn get_width(&self) -> u32 {
        let (w, _) = self.get_size();
        w
    }

    /// Gets the height of the texture.
    #[inline(always)]
    pub fn get_height(&self) -> u32 {
        let (_, h) = self.get_size();
        h
    }
}

impl ImageSize for Texture {
    #[inline(always)]
    fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }
}

impl<D: gfx::Device<C>, C: gfx::CommandBuffer> FromMemoryAlpha<D>
for Texture {
    fn from_memory_alpha(
        device: &mut D,
        buffer: &[u8],
        width: u32,
        height: u32,
        f: |&mut D, Texture| -> Texture
    ) -> Option<Texture> {

        let mut pixels = Vec::new();
        for alpha in buffer.iter() {
            pixels.push(255);
            pixels.push(255);
            pixels.push(255);
            pixels.push(*alpha);
        }

        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::Texture2D,
            format: gfx::tex::RGBA8,
        };

        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).unwrap();
        device.update_texture(&texture, &image_info,
            pixels.as_slice())
            .unwrap();
        Some(f(device, Texture {
            handle: texture
        }))
    }
}

<commit_msg>Made `from_memory_alpha` a normal method<commit_after>use gfx;
use image;
use image::{
    GenericImage,
    ImageBuf,
    Rgba,
};
use texture_lib::ImageSize;

/// Represents a texture.
pub struct Texture {
    /// A handle to the Gfx texture.
    pub handle: gfx::TextureHandle,
}

impl Texture {
    /// Creates a texture from path.
    pub fn from_path<
        C: gfx::CommandBuffer,
        D: gfx::Device<C>
    >(device: &mut D, path: &Path) -> Result<Texture, String> {
        let img = match image::open(path) {
            Ok(img) => img,
            Err(e)  => return Err(format!("Could not load '{}': {}",
                path.filename_str().unwrap(), e)),
        };

        match img.color() {
            image::RGBA(8) => {},
            c => return Err(format!("Unsupported color type {}", c)),
        };

        let (width, height) = img.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).unwrap();
        device.update_texture(&texture, &image_info,
            img.raw_pixels().as_slice())
        .unwrap();

        Ok(Texture {
            handle: texture
        })
    }

    /// Creates a texture from image.
    pub fn from_image<
        C: gfx::CommandBuffer,
        D: gfx::Device<C>
    >(device: &mut D, image: &ImageBuf<Rgba<u8>>) -> Texture {
        let (width, height) = image.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).unwrap();
        device.update_texture(&texture, &image_info,
            image.pixelbuf().as_slice())
        .unwrap();

        Texture {
            handle: texture
        }
    }

    /// Creates a texture from RGBA image.
    pub fn from_rgba8<D: gfx::Device<C>, C: gfx::CommandBuffer>(
        img: ImageBuf<Rgba<u8>>,
        d: &mut D
    ) -> Texture {
        let (width, height) = img.dimensions();

        let mut ti = gfx::tex::TextureInfo::new();
        ti.width = width as u16;
        ti.height = height as u16;
        ti.kind = gfx::tex::Texture2D;
        ti.format = gfx::tex::RGBA8;

        let tex = d.create_texture(ti).unwrap();
        d.update_texture(&tex, &ti.to_image_info(),
                         img.into_vec().as_slice()).unwrap();
        d.generate_mipmap(&tex);

        Texture {
            handle: tex,
        }
    }

    /// Creates texture from memory alpha.
    pub fn from_memory_alpha<
        C: gfx::CommandBuffer,
        D: gfx::Device<C>
    >(
        device: &mut D,
        buffer: &[u8],
        width: u32,
        height: u32,
    ) -> Texture {
        use std::cmp::max;

        let width = max(width, 1);
        let height = max(height, 1);

        let mut pixels = Vec::new();
        for alpha in buffer.iter() {
            pixels.push(255);
            pixels.push(255);
            pixels.push(255);
            pixels.push(*alpha);
        }

        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::Texture2D,
            format: gfx::tex::RGBA8,
        };

        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info).unwrap();
        device.update_texture(&texture, &image_info,
            pixels.as_slice())
            .unwrap();
        Texture {
            handle: texture
        }
    }

    /// Updates the texture with an image.
    pub fn update<
        C: gfx::CommandBuffer,
        D: gfx::Device<C>
    >(&mut self, device: &mut D, image: &ImageBuf<Rgba<u8>>) {
        device.update_texture(&self.handle,
            &self.handle.get_info().to_image_info(),
            image.pixelbuf().as_slice()
        ).unwrap();
    }

    /// Gets the size of the texture.
    #[inline(always)]
    pub fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }

    /// Gets the width of the texture.
    #[inline(always)]
    pub fn get_width(&self) -> u32 {
        let (w, _) = self.get_size();
        w
    }

    /// Gets the height of the texture.
    #[inline(always)]
    pub fn get_height(&self) -> u32 {
        let (_, h) = self.get_size();
        h
    }
}

impl ImageSize for Texture {
    #[inline(always)]
    fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }
}

<|endoftext|>


<commit_before>use std::path::Path;
use { gfx, ImageSize, Settings };
use image::{
    self,
    DynamicImage,
    GenericImage,
    RgbaImage,
};

/// Represents a texture.
#[derive(Clone, Debug, PartialEq)]
pub struct Texture<R> where R: gfx::Resources {
    handle: gfx::TextureHandle<R>
}

impl<R: gfx::Resources> Texture<R> {
    /// Get a handle to the Gfx texture.
    pub fn handle(&self) -> gfx::TextureHandle<R> {
        self.handle.clone()
    }

    /// Creates a texture from path.
    pub fn from_path<F, P>(
        factory: &mut F,
        path: P,
        settings: &Settings,
    ) -> Result<Self, String>
        where F: gfx::Factory<R>,
              P: AsRef<Path>
    {
        let img = try!(image::open(path).map_err(|e| e.to_string()));

        //if settings.force_alpha //TODO
        let img = match img {
            DynamicImage::ImageRgba8(img) => img,
            img => img.to_rgba(),
        };

        let img = if settings.flip_vertical {
            image::imageops::flip_vertical(&img)
        } else {
            img
        };

        let texture = Texture::from_image(factory, &img,
                                          settings.convert_gamma,
                                          settings.compress,
                                          settings.generate_mipmap);
        Ok(texture)
    }

    /// Creates a texture from image.
    pub fn from_image<F>(
        factory: &mut F,
        image: &RgbaImage,
        convert_gamma: bool,
        _compress: bool,
        generate_mipmap: bool
    ) -> Self
        where F: gfx::Factory<R>
    {
        let (width, height) = image.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: if convert_gamma {
                gfx::tex::Format::SRGB8_A8
            } else { gfx::tex::RGBA8 }
        };
        let image_info = texture_info.to_image_info();
        let texture = factory.create_texture(texture_info).unwrap();
        factory.update_texture(&texture, &image_info, &image,
                               Some(gfx::tex::TextureKind::Texture2D)).unwrap();
        if generate_mipmap {
            factory.generate_mipmap(&texture);
        }
        Texture {
            handle: texture
        }
    }

    /// Creates texture from memory alpha.
    pub fn from_memory_alpha<F>(
        factory: &mut F,
        buffer: &[u8],
        width: u32,
        height: u32,
    ) -> Self
        where F: gfx::Factory<R>
    {
        let width = if width == 0 { 1 } else { width };
        let height = if height == 0 { 1 } else { height };

        let mut pixels = vec![];
        for alpha in buffer {
            pixels.extend(vec![255; 3]);
            pixels.push(*alpha);
        }

        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };

        let image_info = texture_info.to_image_info();
        let texture = factory.create_texture(texture_info).unwrap();
        factory.update_texture(&texture, &image_info, &pixels,
                               Some(gfx::tex::TextureKind::Texture2D)).unwrap();
        Texture {
            handle: texture
        }
    }

    /// Updates the texture with an image.
    pub fn update<F>(&mut self, factory: &mut F, image: &RgbaImage)
        where F: gfx::Factory<R>
    {
        factory.update_texture(&self.handle,
            &self.handle.get_info().to_image_info(),
            &image,
            Some(gfx::tex::TextureKind::Texture2D)
        ).unwrap();
    }
}

impl<R> ImageSize for Texture<R> where R: gfx::Resources {
    #[inline(always)]
    fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }
}
<commit_msg>Add empty method<commit_after>use std::path::Path;
use { gfx, ImageSize, Settings };
use image::{
    self,
    DynamicImage,
    GenericImage,
    RgbaImage,
};

/// Represents a texture.
#[derive(Clone, Debug, PartialEq)]
pub struct Texture<R> where R: gfx::Resources {
    handle: gfx::TextureHandle<R>
}

impl<R: gfx::Resources> Texture<R> {
    /// Gets a handle to the Gfx texture.
    pub fn handle(&self) -> gfx::TextureHandle<R> {
        self.handle.clone()
    }

    /// Returns empty texture.
    pub fn empty<F>(factory: &mut F) -> Result<Self, gfx::tex::TextureError>
        where F: gfx::Factory<R>
    {
        let tex_info = gfx::tex::TextureInfo {
            width: 1,
            height: 1,
            depth: 1,
            levels: 1,
            format: gfx::tex::RGBA8,
            kind: gfx::tex::TextureKind::Texture2D
        };
        let ref image_info = tex_info.to_image_info();
        let tex_handle = try!(factory.create_texture(tex_info));
        try!(factory.update_texture(
            &tex_handle,
            image_info,
            &[0u8; 4],
            Some(gfx::tex::TextureKind::Texture2D)
        ));
        Ok(Texture {
            handle: tex_handle
        })
    }

    /// Creates a texture from path.
    pub fn from_path<F, P>(
        factory: &mut F,
        path: P,
        settings: &Settings,
    ) -> Result<Self, String>
        where F: gfx::Factory<R>,
              P: AsRef<Path>
    {
        let img = try!(image::open(path).map_err(|e| e.to_string()));

        //if settings.force_alpha //TODO
        let img = match img {
            DynamicImage::ImageRgba8(img) => img,
            img => img.to_rgba(),
        };

        let img = if settings.flip_vertical {
            image::imageops::flip_vertical(&img)
        } else {
            img
        };

        let texture = Texture::from_image(factory, &img,
                                          settings.convert_gamma,
                                          settings.compress,
                                          settings.generate_mipmap);
        Ok(texture)
    }

    /// Creates a texture from image.
    pub fn from_image<F>(
        factory: &mut F,
        image: &RgbaImage,
        convert_gamma: bool,
        _compress: bool,
        generate_mipmap: bool
    ) -> Self
        where F: gfx::Factory<R>
    {
        let (width, height) = image.dimensions();
        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: if convert_gamma {
                gfx::tex::Format::SRGB8_A8
            } else { gfx::tex::RGBA8 }
        };
        let image_info = texture_info.to_image_info();
        let texture = factory.create_texture(texture_info).unwrap();
        factory.update_texture(&texture, &image_info, &image,
                               Some(gfx::tex::TextureKind::Texture2D)).unwrap();
        if generate_mipmap {
            factory.generate_mipmap(&texture);
        }
        Texture {
            handle: texture
        }
    }

    /// Creates texture from memory alpha.
    pub fn from_memory_alpha<F>(
        factory: &mut F,
        buffer: &[u8],
        width: u32,
        height: u32,
    ) -> Self
        where F: gfx::Factory<R>
    {
        let width = if width == 0 { 1 } else { width };
        let height = if height == 0 { 1 } else { height };

        let mut pixels = vec![];
        for alpha in buffer {
            pixels.extend(vec![255; 3]);
            pixels.push(*alpha);
        }

        let texture_info = gfx::tex::TextureInfo {
            width: width as u16,
            height: height as u16,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };

        let image_info = texture_info.to_image_info();
        let texture = factory.create_texture(texture_info).unwrap();
        factory.update_texture(&texture, &image_info, &pixels,
                               Some(gfx::tex::TextureKind::Texture2D)).unwrap();
        Texture {
            handle: texture
        }
    }

    /// Updates the texture with an image.
    pub fn update<F>(&mut self, factory: &mut F, image: &RgbaImage)
        where F: gfx::Factory<R>
    {
        factory.update_texture(&self.handle,
            &self.handle.get_info().to_image_info(),
            &image,
            Some(gfx::tex::TextureKind::Texture2D)
        ).unwrap();
    }
}

impl<R> ImageSize for Texture<R> where R: gfx::Resources {
    #[inline(always)]
    fn get_size(&self) -> (u32, u32) {
        let info = self.handle.get_info();
        (info.width as u32, info.height as u32)
    }
}
<|endoftext|>


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

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

/// Load an RGBA texture from an image at a path.
pub fn load_rgba_texture<P>(path: P, sampler: &Sampler, linear: bool) -> Result<Texture<Flat, Dim2, RGBA32F>> where P: AsRef<Path> {
  info!("loading texture image: \x1b[35m{:?}", path.as_ref());

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

    if linear {
      y
    } else {
      y.powf(1. / 2.2)
    }
  }).collect();

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

  Ok(tex)
}

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

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

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

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

pub struct TextureImage {
  pub texture: Texture<Flat, Dim2, RGBA32F>,
  sampler: Sampler,
  linear: bool,
}

impl Deref for TextureImage {
  type Target = Texture<Flat, Dim2, RGBA32F>;

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

impl Load for TextureImage {
  type Args = (Sampler, bool);

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

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

impl Reload for TextureImage {
  fn reload_args(&self) -> Self::Args {
    (self.sampler, self.linear)
  }
}
<commit_msg>Better interface for load_rgba_texture.<commit_after>pub use luminance::{Dim2, Flat, R32F, RGBA32F, Sampler, Texture};
use image;
use std::ops::Deref;
use std::path::Path;

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

/// Load an RGBA texture from an image at a path.
pub fn load_rgba_texture<P, L>(path: P, sampler: &Sampler, linearizer: L) -> Result<Texture<Flat, Dim2, RGBA32F>> where P: AsRef<Path>, L: Into<Option<f32>> {
  info!("loading texture image: \x1b[35m{:?}", path.as_ref());

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

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

  Ok(tex)
}

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

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

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

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

pub struct TextureImage {
  pub texture: Texture<Flat, Dim2, RGBA32F>,
  sampler: Sampler,
  linearizer: Option<f32>,
}

impl Deref for TextureImage {
  type Target = Texture<Flat, Dim2, RGBA32F>;

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

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

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

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

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


<commit_before>//! TLC5955 PWM Led driver

use generic_array::{GenericArray, ArrayLength};
use static_ref::Static;
use core::marker::Unsize;
use semihosting::hio;
use core::fmt::Write;
use dma2::{self, DMA, Dma, Buffer, DMAStream};

const CHANNELS_PER_TLC: u8 = 48;
const LEDS_PER_CHIP: u8 = CHANNELS_PER_TLC / 3;
const DC_DATA: u8 = 0x6A;
const DC_BITS: u32 = 7;
const MC_DATA: u8 = 5;
const MC_BITS: u32 = 3;
const BC_DATA: u8 = 110;
const BC_BITS: u32 = 7;

pub trait TLCHardwareLayer {
    fn as_gpio(&self);
    fn as_spi(&self);
    fn latch(&self, delay: u32);
    fn write_bit(&self, bit: u8);
    fn delay(&self, count: u16);
    fn write<B>(&self, tx_buffer: &Static<Buffer<B>>,
        rx_buffer: &Static<Buffer<B>>)where B: Unsize<[u8]>;
    fn wait<B>(&self, buffer: &Static<Buffer<B>>)where B: Unsize<[u8]>;
    fn dump_buffer(&self, buffer: &[u8]);
    fn debug(&self, data: &str);
}
// struct TLC5955<T: FixedSizeArray<u8>> {
// struct TLC5955<N: ArrayLength<u8>> {

struct RGB<T> {
    red: T,
    green: T,
    blue: T,
}

impl Default for RGB<u8> {
    fn default() -> RGB<u8> {
        RGB { red: 0, blue: 0, green: 0}
    }
}

impl Default for RGB<u16> {
    fn default() -> RGB<u16> {
        RGB { red: 0, blue: 0, green: 0}
    }
}

pub struct TLC5955 {
    no_chips: u8,
    function_data: u8,
    brightness: RGB<u8>,
    max_current: RGB<u8>,
}

impl TLC5955 {
    pub fn new(no_chips: u8) -> TLC5955 {
        TLC5955 {
            no_chips: no_chips,
            function_data: 0,
            brightness: Default::default(),
            max_current: Default::default(),
        }
    }

    pub fn setup<B, I>(&mut self,
        tx_buffer: &Static<Buffer<B>>,
        rx_buffer: &Static<Buffer<B>>,
        interface: &I)
        where I: TLCHardwareLayer, B: Unsize<[u8]>
    {
        interface.debug("Sending control register data to TLC5955\n");
        self.fill_control_data(&mut *tx_buffer.borrow_mut());
        interface.dump_buffer(&*tx_buffer.borrow_mut());
        self.send_data(true, tx_buffer, rx_buffer, interface);

        clear_buffer(&mut *tx_buffer.borrow_mut());
        interface.debug("Read data after zeros.\n");
        self.send_data(true, tx_buffer, rx_buffer, interface);

        if !compare_buffers(&*tx_buffer.borrow(), &*rx_buffer.borrow()) {
            interface.debug("Ouch, read control data does not match!\n");
            interface.dump_buffer(&*rx_buffer.borrow_mut());
            loop{
                self.fill_control_data(&mut *tx_buffer.borrow_mut());
        self.send_data(true, tx_buffer, rx_buffer, interface);
            }
        }

        // Send the control data the second time.
        self.fill_control_data(&mut *tx_buffer.borrow_mut());
        self.send_data(true, tx_buffer, rx_buffer, interface);

        {
            let buffer: &mut[u8] = &mut *tx_buffer.borrow_mut();
            // let buffer_slice: &mut[u8] = buffer.lock_mut();
            // Write some random data first 4 leds
            for i in 0..4 {
                // Red
                buffer[(i*6) + 0] =  255;
                buffer[(i*6) + 1] =  255;
                // Green
                buffer[(i*6) + 2] =  0;
                buffer[(i*6) + 3] =  0;
                // Blue
                buffer[(i*6) + 4] =  0;
                buffer[(i*6) + 5] =  0;
            }
        }

        interface.debug("Load GS Data\n");
        interface.dump_buffer(&*tx_buffer.borrow_mut());
        self.send_data(true, tx_buffer, rx_buffer, interface);
        interface.debug("Read GS Data\n");
        interface.dump_buffer(&*rx_buffer.borrow_mut());
        loop {}
    }

    pub fn send_data<B, I>(&self, is_control: bool,
        tx_buffer: &Static<Buffer<B>>,
        rx_buffer: &Static<Buffer<B>>,
        interface: &I)
        where I: TLCHardwareLayer, B: Unsize<[u8]>
    {
        interface.as_gpio();
        if is_control {
            interface.write_bit(1);
        }
        else {
            interface.write_bit(0);
        }

        interface.as_spi();
        interface.write(tx_buffer, rx_buffer);
        interface.wait(tx_buffer);
        interface.wait(rx_buffer);
        interface.latch(1);
    }

    pub fn set_function_data(&mut self, dsprpt: bool, tmgrst: bool, rfresh: bool, espwm: bool, lsdvlt: bool) {
        let mut data: u8 = 0;
        data |= if dsprpt { 1 << 0 } else { 0 };
        data |= if tmgrst { 1 << 0 } else { 0 };
        data |= if rfresh { 1 << 0 } else { 0 };
        data |= if espwm { 1 << 0 } else { 0 };
        data |= if lsdvlt { 1 << 0 } else { 0 };
        self.function_data = data;
    }

    pub fn setall_dcdata<B>(self, buffer: &Static<Buffer<B>>, value: u8)
        where B: Unsize<[u8]> {
        let buffer: &mut[u8] = buffer.lock_mut();

        for index in 0..buffer.len() {
            buffer[index] = value;
        }
    }

    pub fn set_dcdata<B>(self, buffer: &Static<Buffer<B>>,
                                 led_num: u16, red: u8, green: u8, blue: u8)
        where B: Unsize<[u8]> {
        let buffer: &mut[u8] = buffer.lock_mut();
        let index = (led_num * 3) as usize;

        buffer[index] = red;
        buffer[index + 1] = green;
        buffer[index + 2] = blue;
    }

    pub fn set_brightness_current(&mut self, red: u8, green: u8, blue: u8) {
        self.brightness = RGB { red: red, blue: blue, green: green };
    }

    pub fn setall_led<B>(self, buffer: &Static<Buffer<B>>,
                                 red: u16, green: u16, blue: u16)
        where B: Unsize<[u16]> {
        let buffer: &mut[u16] = buffer.lock_mut();
        let no_leds = LEDS_PER_CHIP * self.no_chips;

        for index in 0..no_leds {
            let idx = (index * 3) as usize;
            buffer[idx + 0] = red;
            buffer[idx + 1] = green;
            buffer[idx + 2] = blue;
        }
    }

    pub fn set_led<B>(self, buffer: &Static<Buffer<B>>,
                              led_num: u16, red: u16, green: u16, blue: u16)
        where B: Unsize<[u16]> {
        let buffer: &mut[u16] = buffer.lock_mut();
        let index = (led_num * 3) as usize;

        buffer[index] = red;
        buffer[index + 1] = green;
        buffer[index + 2] = blue;
    }

    pub fn set_max_current(&mut self, red: u8, green: u8, blue: u8) {
        self.max_current = RGB { red: red, blue: blue, green: green };
    }

    fn fill_control_data(&mut self, buffer: &mut[u8]) {
        let mut pos = 0;
        for index in 0..CHANNELS_PER_TLC {
            pos = add_bits(buffer, DC_DATA as u32, DC_BITS, pos);
        }

        for index in 0..3 {
            pos = add_bits(buffer, MC_DATA as u32, MC_BITS, pos);
        }

        for index in 0..3 {
            pos = add_bits(buffer, BC_DATA as u32, BC_BITS, pos);
        }
        pos = add_bits(buffer, 0x0B as u32, 5, pos);
        pos = 760;
        add_bits(buffer, 0x96 as u32, 8, pos);
    }
}

fn add_bits(buffer: &mut[u8], mut val: u32, mut size: u32, mut pos: usize) -> usize {
    let len = buffer.len();
    while (size > 0) {
        if (val & 1) > 0 {
            buffer[len - 1 -  (pos >> 3)] |= 1 << (pos & 7);
        }
        val >>= 1;
        pos += 1;
        size -= 1;
    }
    return pos;
}

fn clear_buffer(buffer: &mut[u8]) {
    for index in 0..buffer.len() {
        buffer[index] = 0;
    }
}

fn compare_buffers(buffer1: &[u8], buffer2: &[u8]) -> bool {
    buffer1.iter().eq(buffer2)
}<commit_msg>Use blocking spi write instead of dma<commit_after>//! TLC5955 PWM Led driver

use generic_array::{GenericArray, ArrayLength};
use static_ref::Static;
use core::marker::Unsize;
use semihosting::hio;
use core::fmt::Write;
use core::ops::DerefMut;
use dma2::{self, DMA, Dma, Buffer, DMAStream};

const CHANNELS_PER_TLC: u8 = 48;
const LEDS_PER_CHIP: u8 = CHANNELS_PER_TLC / 3;
const DC_DATA: u8 = 0x6A;
const DC_BITS: u32 = 7;
const MC_DATA: u8 = 5;
const MC_BITS: u32 = 3;
const BC_DATA: u8 = 110;
const BC_BITS: u32 = 7;

pub trait TLCHardwareLayer {
    fn as_gpio(&self);
    fn as_spi(&self);
    fn latch(&self, delay: u32);
    fn write_bit(&self, bit: u8);
    fn delay(&self, count: u16);
    fn read_write_byte(&self, byte: u8) -> u8;
    fn write<B>(&self, tx_buffer: &Static<Buffer<B>>,
        rx_buffer: &Static<Buffer<B>>)where B: Unsize<[u8]>;
    fn wait<B>(&self, buffer: &Static<Buffer<B>>)where B: Unsize<[u8]>;
    fn dump_buffer(&self, buffer: &[u8]);
    fn debug(&self, data: &str);
}
// struct TLC5955<T: FixedSizeArray<u8>> {
// struct TLC5955<N: ArrayLength<u8>> {

struct RGB<T> {
    red: T,
    green: T,
    blue: T,
}

impl Default for RGB<u8> {
    fn default() -> RGB<u8> {
        RGB { red: 0, blue: 0, green: 0}
    }
}

impl Default for RGB<u16> {
    fn default() -> RGB<u16> {
        RGB { red: 0, blue: 0, green: 0}
    }
}

pub struct TLC5955 {
    no_chips: u8,
    function_data: u8,
    brightness: RGB<u8>,
    max_current: RGB<u8>,
}

impl TLC5955 {
    pub fn new(no_chips: u8) -> TLC5955 {
        TLC5955 {
            no_chips: no_chips,
            function_data: 0,
            brightness: Default::default(),
            max_current: Default::default(),
        }
    }

    pub fn setup<B, I>(&mut self,
        tx_buffer: &Static<Buffer<B>>,
        rx_buffer: &Static<Buffer<B>>,
        interface: &I)
        where I: TLCHardwareLayer, B: Unsize<[u8]>
    {
        interface.debug("Sending control register data to TLC5955\n");
        self.fill_control_data(&mut *tx_buffer.borrow_mut());
        interface.dump_buffer(&*tx_buffer.borrow_mut());
        self.send_data(true, tx_buffer, rx_buffer, interface);

        clear_buffer(&mut *tx_buffer.borrow_mut());
        interface.debug("Read data after zeros.\n");
        self.send_data(true, tx_buffer, rx_buffer, interface);
        shift_buffer_by_7(&mut *rx_buffer.borrow_mut());

        self.fill_control_data(&mut *tx_buffer.borrow_mut());
        if !compare_buffers(&*tx_buffer.borrow(), &*rx_buffer.borrow()) {
            interface.debug("Ouch, read control data does not match!\n");
            interface.dump_buffer(&*rx_buffer.borrow_mut());
            loop {}
        }

        // Send the control data the second time.
        self.send_data(true, tx_buffer, rx_buffer, interface);

        clear_buffer(&mut *tx_buffer.borrow_mut());
        {
            let buffer: &mut[u8] = &mut *tx_buffer.borrow_mut();
            buffer[72] = 255;
            buffer[70] = 150;
            buffer[68] = 210;
        }

        interface.debug("Load GS Data\n");
        interface.dump_buffer(&*tx_buffer.borrow_mut());
        self.send_data(true, tx_buffer, rx_buffer, interface);
        interface.debug("Read GS Data\n");
        interface.dump_buffer(&*rx_buffer.borrow_mut());
        loop {}
    }

    pub fn send_data<B, I>(&self, is_control: bool,
        tx_buffer: &Static<Buffer<B>>,
        rx_buffer: &Static<Buffer<B>>,
        interface: &I)
        where I: TLCHardwareLayer, B: Unsize<[u8]>
    {
        /*
        interface.as_gpio();
        if is_control {
            interface.write_bit(1);
        }
        else {
            interface.write_bit(0);
        }
        interface.as_spi();
        */
        {
            let tx_buff: &mut [u8] = &mut *tx_buffer.borrow_mut();
            let rx_buff: &mut [u8] = &mut *rx_buffer.borrow_mut();

            for (txb, rxb) in tx_buff.iter_mut().zip(rx_buff.iter_mut()) {
                *rxb = interface.read_write_byte(*txb);
            }
        }

        // interface.write(tx_buffer, rx_buffer);
        // interface.wait(tx_buffer);
        // interface.wait(rx_buffer);
        interface.latch(1);
    }

    pub fn set_function_data(&mut self, dsprpt: bool, tmgrst: bool, rfresh: bool, espwm: bool, lsdvlt: bool) {
        let mut data: u8 = 0;
        data |= if dsprpt { 1 << 0 } else { 0 };
        data |= if tmgrst { 1 << 0 } else { 0 };
        data |= if rfresh { 1 << 0 } else { 0 };
        data |= if espwm { 1 << 0 } else { 0 };
        data |= if lsdvlt { 1 << 0 } else { 0 };
        self.function_data = data;
    }

    pub fn setall_dcdata<B>(self, buffer: &Static<Buffer<B>>, value: u8)
        where B: Unsize<[u8]> {
        let buffer: &mut[u8] = buffer.lock_mut();

        for index in 0..buffer.len() {
            buffer[index] = value;
        }
    }

    pub fn set_dcdata<B>(self, buffer: &Static<Buffer<B>>,
                                 led_num: u16, red: u8, green: u8, blue: u8)
        where B: Unsize<[u8]> {
        let buffer: &mut[u8] = buffer.lock_mut();
        let index = (led_num * 3) as usize;

        buffer[index] = red;
        buffer[index + 1] = green;
        buffer[index + 2] = blue;
    }

    pub fn set_brightness_current(&mut self, red: u8, green: u8, blue: u8) {
        self.brightness = RGB { red: red, blue: blue, green: green };
    }

    pub fn setall_led<B>(self, buffer: &Static<Buffer<B>>,
                                 red: u16, green: u16, blue: u16)
        where B: Unsize<[u16]> {
        let buffer: &mut[u16] = buffer.lock_mut();
        let no_leds = LEDS_PER_CHIP * self.no_chips;

        for index in 0..no_leds {
            let idx = (index * 3) as usize;
            buffer[idx + 0] = red;
            buffer[idx + 1] = green;
            buffer[idx + 2] = blue;
        }
    }

    pub fn set_led<B>(self, buffer: &Static<Buffer<B>>,
                              led_num: u16, red: u16, green: u16, blue: u16)
        where B: Unsize<[u16]> {
        let buffer: &mut[u16] = buffer.lock_mut();
        let index = (led_num * 3) as usize;

        buffer[index] = red;
        buffer[index + 1] = green;
        buffer[index + 2] = blue;
    }

    pub fn set_max_current(&mut self, red: u8, green: u8, blue: u8) {
        self.max_current = RGB { red: red, blue: blue, green: green };
    }

    fn fill_control_data(&mut self, buffer: &mut[u8]) {
        let mut pos = 0;
        for index in 0..CHANNELS_PER_TLC {
            pos = add_bits(buffer, DC_DATA as u32, DC_BITS, pos);
        }

        for index in 0..3 {
            pos = add_bits(buffer, MC_DATA as u32, MC_BITS, pos);
        }

        for index in 0..3 {
            pos = add_bits(buffer, BC_DATA as u32, BC_BITS, pos);
        }
        pos = add_bits(buffer, 0x0B as u32, 5, pos);
        pos = 760;
        add_bits(buffer, 0x196 as u32, 9, pos);
    }
}

fn add_bits(buffer: &mut[u8], mut val: u32, mut size: u32, mut pos: usize) -> usize {
    let len = buffer.len();
    while (size > 0) {
        if (val & 1) > 0 {
            buffer[len - 1 -  (pos >> 3)] |= 1 << (pos & 7);
        }
        val >>= 1;
        pos += 1;
        size -= 1;
    }
    return pos;
}

fn clear_buffer(buffer: &mut[u8]) {
    for index in 0..buffer.len() {
        buffer[index] = 0;
    }
}

fn compare_buffers(buffer1: &[u8], buffer2: &[u8]) -> bool {
    buffer1.iter().eq(buffer2)
}

fn shift_buffer_by_7(buffer: &mut [u8]) {
    let mut old: u8 = 0;
    for index in 0..buffer.len() {
        let mut val = buffer[index];
        buffer[index] = old.wrapping_shl(1) | val.wrapping_shr(7);
        old = val;
    }
}<|endoftext|>