<commit_before>#[cfg(target_arch = "wasm32")]
macro_rules! check {
    ($check:expr) => {{
        $check
    }};
}

#[cfg(not(target_arch = "wasm32"))]
macro_rules! check {
    ($check:expr) => {{
        let result = $check;
        if br::ScInternalResult::Success != result {
            if br::ScInternalResult::CompilationError == result {
                let mut message_ptr = ptr::null();

                if br::ScInternalResult::Success
                    != br::sc_internal_get_latest_exception_message(&mut message_ptr)
                {
                    return Err(ErrorCode::Unhandled);
                }

                let message = match std::ffi::CStr::from_ptr(message_ptr)
                    .to_owned()
                    .into_string()
                {
                    Err(_) => return Err(ErrorCode::Unhandled),
                    Ok(v) => v,
                };

                if br::ScInternalResult::Success
                    != br::sc_internal_free_pointer(message_ptr as *mut std::os::raw::c_void)
                {
                    return Err(ErrorCode::Unhandled);
                }

                return Err(ErrorCode::CompilationError(message));
            }

            return Err(ErrorCode::Unhandled);
        }
    }};
}

mod compiler;

#[cfg(feature = "glsl")]
pub mod glsl;
#[cfg(feature = "hlsl")]
pub mod hlsl;
#[cfg(feature = "msl")]
pub mod msl;

pub mod spirv;

#[cfg(target_arch = "wasm32")]
pub(crate) mod emscripten;
pub(crate) mod ptr_util;

#[cfg(target_arch = "wasm32")]
mod bindings_wasm_functions;

#[cfg(target_arch = "wasm32")]
mod bindings {
    #![allow(dead_code)]
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]
    include!(concat!("bindings_wasm.rs"));
    pub use crate::bindings_wasm_functions::*;
    pub use root::*;
}

#[cfg(not(target_arch = "wasm32"))]
mod bindings {
    #![allow(dead_code)]
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]
    include!(concat!("bindings_native.rs"));
    pub use root::*;
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum ErrorCode {
    Unhandled,
    CompilationError(String),
}
<commit_msg>Fix wasm32 build on windows and metal When another crate disabled on wasm32 still activates hlsl or msl feature<commit_after>#[cfg(target_arch = "wasm32")]
macro_rules! check {
    ($check:expr) => {{
        $check
    }};
}

#[cfg(not(target_arch = "wasm32"))]
macro_rules! check {
    ($check:expr) => {{
        let result = $check;
        if br::ScInternalResult::Success != result {
            if br::ScInternalResult::CompilationError == result {
                let mut message_ptr = ptr::null();

                if br::ScInternalResult::Success
                    != br::sc_internal_get_latest_exception_message(&mut message_ptr)
                {
                    return Err(ErrorCode::Unhandled);
                }

                let message = match std::ffi::CStr::from_ptr(message_ptr)
                    .to_owned()
                    .into_string()
                {
                    Err(_) => return Err(ErrorCode::Unhandled),
                    Ok(v) => v,
                };

                if br::ScInternalResult::Success
                    != br::sc_internal_free_pointer(message_ptr as *mut std::os::raw::c_void)
                {
                    return Err(ErrorCode::Unhandled);
                }

                return Err(ErrorCode::CompilationError(message));
            }

            return Err(ErrorCode::Unhandled);
        }
    }};
}

mod compiler;

#[cfg(feature = "glsl")]
pub mod glsl;
#[cfg(all(feature = "hlsl", not(target_arch = "wasm32")))]
pub mod hlsl;
#[cfg(all(feature = "msl", not(target_arch = "wasm32")))]
pub mod msl;

pub mod spirv;

#[cfg(target_arch = "wasm32")]
pub(crate) mod emscripten;
pub(crate) mod ptr_util;

#[cfg(target_arch = "wasm32")]
mod bindings_wasm_functions;

#[cfg(target_arch = "wasm32")]
mod bindings {
    #![allow(dead_code)]
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]
    include!(concat!("bindings_wasm.rs"));
    pub use crate::bindings_wasm_functions::*;
    pub use root::*;
}

#[cfg(not(target_arch = "wasm32"))]
mod bindings {
    #![allow(dead_code)]
    #![allow(non_upper_case_globals)]
    #![allow(non_camel_case_types)]
    #![allow(non_snake_case)]
    include!(concat!("bindings_native.rs"));
    pub use root::*;
}

#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub enum ErrorCode {
    Unhandled,
    CompilationError(String),
}
<|endoftext|>


<commit_before>#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]

use tokio::fs::File;
use tokio::prelude::*;

use std::io::prelude::*;
use tempfile::NamedTempFile;

const HELLO: &[u8] = b"hello world...";

#[tokio::test]
async fn basic_read() {
    let mut tempfile = tempfile();
    tempfile.write_all(HELLO).unwrap();

    let mut file = File::open(tempfile.path()).await.unwrap();

    let mut buf = [0; 1024];
    let n = file.read(&mut buf).await.unwrap();

    assert_eq!(n, HELLO.len());
    assert_eq!(&buf[..n], HELLO);
}

#[tokio::test]
async fn basic_write() {
    let tempfile = tempfile();

    let mut file = File::create(tempfile.path()).await.unwrap();

    file.write_all(HELLO).await.unwrap();
    file.flush().await.unwrap();

    let file = std::fs::read(tempfile.path()).unwrap();
    assert_eq!(file, HELLO);
}

fn tempfile() -> NamedTempFile {
    NamedTempFile::new().unwrap()
}
<commit_msg>Add unit tests for tokio::File::AsRaw{Fd,Handle} for Unix and Windows. (#1890)<commit_after>#![warn(rust_2018_idioms)]
#![cfg(feature = "full")]

use tokio::fs::File;
use tokio::prelude::*;

use std::io::prelude::*;
use tempfile::NamedTempFile;

const HELLO: &[u8] = b"hello world...";

#[tokio::test]
async fn basic_read() {
    let mut tempfile = tempfile();
    tempfile.write_all(HELLO).unwrap();

    let mut file = File::open(tempfile.path()).await.unwrap();

    let mut buf = [0; 1024];
    let n = file.read(&mut buf).await.unwrap();

    assert_eq!(n, HELLO.len());
    assert_eq!(&buf[..n], HELLO);
}

#[tokio::test]
async fn basic_write() {
    let tempfile = tempfile();

    let mut file = File::create(tempfile.path()).await.unwrap();

    file.write_all(HELLO).await.unwrap();
    file.flush().await.unwrap();

    let file = std::fs::read(tempfile.path()).unwrap();
    assert_eq!(file, HELLO);
}

fn tempfile() -> NamedTempFile {
    NamedTempFile::new().unwrap()
}

#[tokio::test]
#[cfg(unix)]
async fn unix_fd() {
    use std::os::unix::io::AsRawFd;
    let tempfile = tempfile();

    let file = File::create(tempfile.path()).await.unwrap();
    assert!(file.as_raw_fd() as u64 > 0);
}

#[tokio::test]
#[cfg(windows)]
async fn windows_handle() {
    use std::os::windows::io::AsRawHandle;
    let tempfile = tempfile();

    let file = File::create(tempfile.path()).await.unwrap();
    assert!(file.as_raw_handle() as u64 > 0);
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use LinkerFlavor;
use target::{Target, TargetOptions, TargetResult};

pub fn target() -> TargetResult {
    let mut base = super::android_base::opts();
    base.features = "+v7,+vfp3,+d16".to_string();
    base.max_atomic_width = Some(64);

    Ok(Target {
        llvm_target: "arm-linux-androideabi".to_string(),
        target_endian: "little".to_string(),
        target_pointer_width: "32".to_string(),
        data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
        arch: "arm".to_string(),
        target_os: "android".to_string(),
        target_env: "".to_string(),
        target_vendor: "unknown".to_string(),
        linker_flavor: LinkerFlavor::Gcc,
        options: TargetOptions {
            abi_blacklist: super::arm_base::abi_blacklist(),
            .. base
        },
    })
}
<commit_msg>Change arm-linux-androideabi to correspond to the armeabi official ABI<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use LinkerFlavor;
use target::{Target, TargetOptions, TargetResult};

pub fn target() -> TargetResult {
    let mut base = super::android_base::opts();
    // https://developer.android.com/ndk/guides/abis.html#armeabi
    base.features = "+v5te".to_string();
    base.max_atomic_width = Some(64);

    Ok(Target {
        llvm_target: "arm-linux-androideabi".to_string(),
        target_endian: "little".to_string(),
        target_pointer_width: "32".to_string(),
        data_layout: "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
        arch: "arm".to_string(),
        target_os: "android".to_string(),
        target_env: "".to_string(),
        target_vendor: "unknown".to_string(),
        linker_flavor: LinkerFlavor::Gcc,
        options: TargetOptions {
            abi_blacklist: super::arm_base::abi_blacklist(),
            .. base
        },
    })
}
<|endoftext|>


<commit_before>use std::io;
use std::to_str::ToStr;
use std::os::pipe;
use std::io::{Open, Write};
use std::io::fs::{mkdir, File};
use std::io::pipe::PipeStream;
use std::io::stdio::{stdout, stderr};

use argparse::{ArgumentParser, Store, List};

use super::env::Environ;
use super::config::Config;
use super::linux::{make_namespace, change_root, bind_mount, mount_pseudofs};
use super::linux::{execute, forkme, wait_process};
use libc::funcs::posix88::unistd::getuid;

fn mount_all(root: &Path, mount_dir: &Path, project_root: &Path)
    -> Result<(), String>
{
    let vagga_marker = mount_dir.join_many(["tmp", ".vagga"]);
    try!(bind_mount(root, mount_dir, false));
    try!(bind_mount(&Path::new("/sys"), &mount_dir.join("sys"), false));
    // TODO(tailhook) use dev in /var/lib/container-dev
    try!(bind_mount(&Path::new("/dev"), &mount_dir.join("dev"), false));
    try!(bind_mount(project_root, &mount_dir.join("work"), true));
    try!(mount_pseudofs("proc", &mount_dir.join("proc"), ""));
    // TODO(tailhook) allow customize size of tmpfs
    try!(mount_pseudofs("tmpfs", &mount_dir.join("tmp"),
                        "size=102400k,mode=1777"));
    try!(ensure_dir(&vagga_marker));
    try!(bind_mount(&vagga_marker,
                    &mount_dir.join_many(["work", ".vagga"]),
                    false));
    match File::open_mode(&vagga_marker.join("CONTAINED.txt"), Open, Write)
        .write_line("You are running in vagga container.") {
        Ok(()) => {}
        Err(e) => return Err(format!("Can't write CONTAINED.txt: {}", e)),
    }
    return Ok(());
}

fn ensure_dir(p: &Path) -> Result<(),String> {
    if p.exists() {
        return Ok(());
    }
    return mkdir(p, io::UserRWX).map_err(|e| { e.to_str() });
}

pub fn run_chroot(env: &Environ, container_root: &Path, mount_dir: &Path,
    command: &String, args: &Vec<String>)
    -> Result<(),String>
{
    try!(mount_all(container_root, mount_dir, &env.project_root));
    try!(change_root(mount_dir));
    // TODO(tailhook) set environment from config
    let environ = vec!("PATH=/bin:/usr/bin:/usr/local/bin".to_string());
    try!(execute(command, args, &environ));
    unreachable!();
}

pub fn run_user_command(env: &Environ, config: &Config,
    cmdname: &String, args: Vec<String>)
    -> Result<int, String> {
    unimplemented!();
}

pub fn run_command(env: &Environ, config: &Config, args: Vec<String>)
    -> Result<int, String>
{
    let mut cname = "devel".to_string();
    let mut command = "".to_string();
    let mut cmdargs = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut cname)
            .add_argument("container", box Store::<String>,
                "A name of the container to build")
            .required();
        ap.refer(&mut command)
            .add_argument("command", box Store::<String>,
                "A command to run inside container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arguments", box List::<String>,
                "Arguments for the command")
            .required();
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} in config",
                               cname));
        }
    };
    info!("Running {}: {} {}", cname, command, cmdargs);

    let container_dir = env.project_root.join_many(
        [".vagga", cname.as_slice()]);
    let container_root = container_dir.join("root");
    let uid = unsafe { getuid() };

    for dir in ["proc", "sys", "dev", "work", "tmp"].iter() {
        try!(ensure_dir(&container_root.join(*dir)));
    }

    let mount_dir = env.project_root.join_many([".vagga", "mnt"]);
    try!(ensure_dir(&mount_dir));
    try!(make_namespace());

    let mut pipe = match PipeStream::pair() {
        Ok(pipe) => pipe,
        Err(x) => return Err(format!("Can't create pipe: {}", x)),
    };

    let pid = try!(forkme());
    if pid == 0 {
        let mut buf: [u8,..1] = [0];
        match pipe.reader.read(buf) {
            Ok(_) => {}
            Err(x) => return Err(format!("Can't read from pipe: {}", x)),
        }
        drop(pipe);
        try!(run_chroot(env, &container_root, &mount_dir, &command, &cmdargs));
        unreachable!();
    } else {
        // TODO(tailhook) set uid map from config
        let uid_map = format!("0 {} 1", uid);
        debug!("Writing uid_map: {}", uid_map);
        match File::open_mode(&Path::new("/proc")
                          .join(pid.to_str())
                          .join("uid_map"), Open, Write)
                .write_str(uid_map.as_slice()) {
            Ok(()) => {}
            Err(e) => return Err(format!(
                "Error writing uid mapping: {}", e)),
        }

        match pipe.writer.write_char('x') {
            Ok(_) => {}
            Err(e) => return Err(format!(
                "Error writing to pipe: {}", e)),
        }
        drop(pipe);

        return wait_process(pid);
    }
}
<commit_msg>Running custom user commands implemented<commit_after>use std::io;
use std::to_str::ToStr;
use std::os::pipe;
use std::io::{Open, Write};
use std::io::fs::{mkdir, File};
use std::io::pipe::PipeStream;
use std::io::stdio::{stdout, stderr};

use argparse::{ArgumentParser, Store, List};

use super::env::Environ;
use super::config::Config;
use super::linux::{make_namespace, change_root, bind_mount, mount_pseudofs};
use super::linux::{execute, forkme, wait_process};
use libc::funcs::posix88::unistd::getuid;

fn mount_all(root: &Path, mount_dir: &Path, project_root: &Path)
    -> Result<(), String>
{
    let vagga_marker = mount_dir.join_many(["tmp", ".vagga"]);
    try!(bind_mount(root, mount_dir, false));
    try!(bind_mount(&Path::new("/sys"), &mount_dir.join("sys"), false));
    // TODO(tailhook) use dev in /var/lib/container-dev
    try!(bind_mount(&Path::new("/dev"), &mount_dir.join("dev"), false));
    try!(bind_mount(project_root, &mount_dir.join("work"), true));
    try!(mount_pseudofs("proc", &mount_dir.join("proc"), ""));
    // TODO(tailhook) allow customize size of tmpfs
    try!(mount_pseudofs("tmpfs", &mount_dir.join("tmp"),
                        "size=102400k,mode=1777"));
    try!(ensure_dir(&vagga_marker));
    try!(bind_mount(&vagga_marker,
                    &mount_dir.join_many(["work", ".vagga"]),
                    false));
    match File::open_mode(&vagga_marker.join("CONTAINED.txt"), Open, Write)
        .write_line("You are running in vagga container.") {
        Ok(()) => {}
        Err(e) => return Err(format!("Can't write CONTAINED.txt: {}", e)),
    }
    return Ok(());
}

fn ensure_dir(p: &Path) -> Result<(),String> {
    if p.exists() {
        return Ok(());
    }
    return mkdir(p, io::UserRWX).map_err(|e| { e.to_str() });
}

pub fn run_chroot(env: &Environ, container_root: &Path, mount_dir: &Path,
    command: &String, args: &Vec<String>)
    -> Result<(),String>
{
    try!(mount_all(container_root, mount_dir, &env.project_root));
    try!(change_root(mount_dir));
    // TODO(tailhook) set environment from config
    let environ = vec!("PATH=/bin:/usr/bin:/usr/local/bin".to_string());
    try!(execute(command, args, &environ));
    unreachable!();
}

pub fn run_user_command(env: &Environ, config: &Config,
    cmdname: &String, args: Vec<String>)
    -> Result<int, String>
{
    let command = match config.commands.find(cmdname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find command {} in config",
                               cmdname));
        }
    };
    let cname = match command.container {
        Some(ref name) => name.clone(),
        None => unimplemented!(),
    };
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} for command {}",
                               command.container, cmdname));
        }
    };
    return match command.run {
        Some(ref cmdline) => _run(env, config, &cname,
            &"/bin/sh".to_string(),
            &(vec!("-c".to_string(), cmdline.clone()) + args.slice_from(1))),
        None => unimplemented!()
    }
}

pub fn run_command(env: &Environ, config: &Config, args: Vec<String>)
    -> Result<int, String>
{
    let mut cname = "devel".to_string();
    let mut command = "".to_string();
    let mut cmdargs = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut cname)
            .add_argument("container", box Store::<String>,
                "A name of the container to build")
            .required();
        ap.refer(&mut command)
            .add_argument("command", box Store::<String>,
                "A command to run inside container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arguments", box List::<String>,
                "Arguments for the command")
            .required();
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} in config",
                               cname));
        }
    };
    _run(env, config, &cname, &command, &cmdargs)
}

pub fn _run(env: &Environ, config: &Config, container: &String,
    command: &String, cmdargs: &Vec<String>)
    -> Result<int, String>
{
    info!("Running {}: {} {}", container, command, cmdargs);
    let container_dir = env.project_root.join_many(
        [".vagga", container.as_slice()]);
    let container_root = container_dir.join("root");
    let uid = unsafe { getuid() };

    for dir in ["proc", "sys", "dev", "work", "tmp"].iter() {
        try!(ensure_dir(&container_root.join(*dir)));
    }

    let mount_dir = env.project_root.join_many([".vagga", "mnt"]);
    try!(ensure_dir(&mount_dir));
    try!(make_namespace());

    let mut pipe = match PipeStream::pair() {
        Ok(pipe) => pipe,
        Err(x) => return Err(format!("Can't create pipe: {}", x)),
    };

    let pid = try!(forkme());
    if pid == 0 {
        let mut buf: [u8,..1] = [0];
        match pipe.reader.read(buf) {
            Ok(_) => {}
            Err(x) => return Err(format!("Can't read from pipe: {}", x)),
        }
        drop(pipe);
        try!(run_chroot(env, &container_root, &mount_dir, command, cmdargs));
        unreachable!();
    } else {
        // TODO(tailhook) set uid map from config
        let uid_map = format!("0 {} 1", uid);
        debug!("Writing uid_map: {}", uid_map);
        match File::open_mode(&Path::new("/proc")
                          .join(pid.to_str())
                          .join("uid_map"), Open, Write)
                .write_str(uid_map.as_slice()) {
            Ok(()) => {}
            Err(e) => return Err(format!(
                "Error writing uid mapping: {}", e)),
        }

        match pipe.writer.write_char('x') {
            Ok(_) => {}
            Err(e) => return Err(format!(
                "Error writing to pipe: {}", e)),
        }
        drop(pipe);

        return wait_process(pid);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_name = "alloc_jemalloc"]
#![crate_type = "rlib"]
#![no_std]
#![allocator]
#![unstable(feature = "alloc_jemalloc",
            reason = "this library is unlikely to be stabilized in its current \
                      form or name",
            issue = "27783")]
#![cfg_attr(not(stage0), deny(warnings))]
#![feature(allocator)]
#![feature(libc)]
#![feature(staged_api)]

extern crate libc;

use libc::{c_int, c_void, size_t};

// Linkage directives to pull in jemalloc and its dependencies.
//
// On some platforms we need to be sure to link in `pthread` which jemalloc
// depends on, and specifically on android we need to also link to libgcc.
// Currently jemalloc is compiled with gcc which will generate calls to
// intrinsics that are libgcc specific (e.g. those intrinsics aren't present in
// libcompiler-rt), so link that in to get that support.
#[link(name = "jemalloc", kind = "static")]
#[cfg_attr(target_os = "android", link(name = "gcc"))]
#[cfg_attr(all(not(windows),
               not(target_os = "android"),
               not(target_env = "musl")),
           link(name = "pthread"))]
#[cfg(not(cargobuild))]
extern {}

// Note that the symbols here are prefixed by default on OSX (we don't
// explicitly request it), and on Android and DragonFly we explicitly request
// it as unprefixing cause segfaults (mismatches in allocators).
extern {
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly"),
               link_name = "je_mallocx")]
    fn mallocx(size: size_t, flags: c_int) -> *mut c_void;
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly"),
               link_name = "je_rallocx")]
    fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly"),
               link_name = "je_xallocx")]
    fn xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly"),
               link_name = "je_sdallocx")]
    fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly"),
               link_name = "je_nallocx")]
    fn nallocx(size: size_t, flags: c_int) -> size_t;
}

// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values. In practice, the alignment is a
// constant at the call site and the branch will be optimized out.
#[cfg(all(any(target_arch = "arm",
              target_arch = "mips",
              target_arch = "powerpc")))]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "aarch64",
              target_arch = "powerpc64")))]
const MIN_ALIGN: usize = 16;

// MALLOCX_ALIGN(a) macro
fn mallocx_align(a: usize) -> c_int {
    a.trailing_zeros() as c_int
}

fn align_to_flags(align: usize) -> c_int {
    if align <= MIN_ALIGN {
        0
    } else {
        mallocx_align(align)
    }
}

#[no_mangle]
pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
    let flags = align_to_flags(align);
    unsafe { mallocx(size as size_t, flags) as *mut u8 }
}

#[no_mangle]
pub extern "C" fn __rust_reallocate(ptr: *mut u8,
                                    _old_size: usize,
                                    size: usize,
                                    align: usize)
                                    -> *mut u8 {
    let flags = align_to_flags(align);
    unsafe { rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 }
}

#[no_mangle]
pub extern "C" fn __rust_reallocate_inplace(ptr: *mut u8,
                                            _old_size: usize,
                                            size: usize,
                                            align: usize)
                                            -> usize {
    let flags = align_to_flags(align);
    unsafe { xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize }
}

#[no_mangle]
pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
    let flags = align_to_flags(align);
    unsafe { sdallocx(ptr as *mut c_void, old_size as size_t, flags) }
}

#[no_mangle]
pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize {
    let flags = align_to_flags(align);
    unsafe { nallocx(size as size_t, flags) as usize }
}

// These symbols are used by jemalloc on android but the really old android
// we're building on doesn't have them defined, so just make sure the symbols
// are available.
#[no_mangle]
#[cfg(target_os = "android")]
pub extern fn pthread_atfork(_prefork: *mut u8,
                             _postfork_parent: *mut u8,
                             _postfork_child: *mut u8) -> i32 {
    0
}
<commit_msg>Fix alloc_jemalloc on windows gnu targets<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![crate_name = "alloc_jemalloc"]
#![crate_type = "rlib"]
#![no_std]
#![allocator]
#![unstable(feature = "alloc_jemalloc",
            reason = "this library is unlikely to be stabilized in its current \
                      form or name",
            issue = "27783")]
#![cfg_attr(not(stage0), deny(warnings))]
#![feature(allocator)]
#![feature(libc)]
#![feature(staged_api)]

extern crate libc;

use libc::{c_int, c_void, size_t};

// Linkage directives to pull in jemalloc and its dependencies.
//
// On some platforms we need to be sure to link in `pthread` which jemalloc
// depends on, and specifically on android we need to also link to libgcc.
// Currently jemalloc is compiled with gcc which will generate calls to
// intrinsics that are libgcc specific (e.g. those intrinsics aren't present in
// libcompiler-rt), so link that in to get that support.
#[link(name = "jemalloc", kind = "static")]
#[cfg_attr(target_os = "android", link(name = "gcc"))]
#[cfg_attr(all(not(windows),
               not(target_os = "android"),
               not(target_env = "musl")),
           link(name = "pthread"))]
#[cfg(not(cargobuild))]
extern {}

// Note that the symbols here are prefixed by default on OSX and Windows (we
// don't explicitly request it), and on Android and DragonFly we explicitly
// request it as unprefixing cause segfaults (mismatches in allocators).
extern {
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly", target_os = "windows"),
               link_name = "je_mallocx")]
    fn mallocx(size: size_t, flags: c_int) -> *mut c_void;
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly", target_os = "windows"),
               link_name = "je_rallocx")]
    fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void;
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly", target_os = "windows"),
               link_name = "je_xallocx")]
    fn xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t;
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly", target_os = "windows"),
               link_name = "je_sdallocx")]
    fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int);
    #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios",
                   target_os = "dragonfly", target_os = "windows"),
               link_name = "je_nallocx")]
    fn nallocx(size: size_t, flags: c_int) -> size_t;
}

// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values. In practice, the alignment is a
// constant at the call site and the branch will be optimized out.
#[cfg(all(any(target_arch = "arm",
              target_arch = "mips",
              target_arch = "powerpc")))]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86",
              target_arch = "x86_64",
              target_arch = "aarch64",
              target_arch = "powerpc64")))]
const MIN_ALIGN: usize = 16;

// MALLOCX_ALIGN(a) macro
fn mallocx_align(a: usize) -> c_int {
    a.trailing_zeros() as c_int
}

fn align_to_flags(align: usize) -> c_int {
    if align <= MIN_ALIGN {
        0
    } else {
        mallocx_align(align)
    }
}

#[no_mangle]
pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
    let flags = align_to_flags(align);
    unsafe { mallocx(size as size_t, flags) as *mut u8 }
}

#[no_mangle]
pub extern "C" fn __rust_reallocate(ptr: *mut u8,
                                    _old_size: usize,
                                    size: usize,
                                    align: usize)
                                    -> *mut u8 {
    let flags = align_to_flags(align);
    unsafe { rallocx(ptr as *mut c_void, size as size_t, flags) as *mut u8 }
}

#[no_mangle]
pub extern "C" fn __rust_reallocate_inplace(ptr: *mut u8,
                                            _old_size: usize,
                                            size: usize,
                                            align: usize)
                                            -> usize {
    let flags = align_to_flags(align);
    unsafe { xallocx(ptr as *mut c_void, size as size_t, 0, flags) as usize }
}

#[no_mangle]
pub extern "C" fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
    let flags = align_to_flags(align);
    unsafe { sdallocx(ptr as *mut c_void, old_size as size_t, flags) }
}

#[no_mangle]
pub extern "C" fn __rust_usable_size(size: usize, align: usize) -> usize {
    let flags = align_to_flags(align);
    unsafe { nallocx(size as size_t, flags) as usize }
}

// These symbols are used by jemalloc on android but the really old android
// we're building on doesn't have them defined, so just make sure the symbols
// are available.
#[no_mangle]
#[cfg(target_os = "android")]
pub extern fn pthread_atfork(_prefork: *mut u8,
                             _postfork_parent: *mut u8,
                             _postfork_child: *mut u8) -> i32 {
    0
}
<|endoftext|>


<commit_before>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the SPI peripherals.
//!
//! RPPAL provides access to the available SPI peripherals by using the `/dev/spidevB.C`
//! devices, where B points to an SPI bus (0, 1, 2), and C to a Chip Enable pin (0, 1, 2).
//!
//! The Raspberry Pi's GPIO header exposes several SPI buses. SPI0 is available
//! on all Raspberry Pi models. SPI1 is available on models with a 40-pin
//! header. SPI2 is only available on the Compute and Compute 3.
//!
//! SPI0 is disabled by default. You can enable it by running
//! `sudo raspi-config`, or by manually adding `dtparam=spi=on` to
//! `/boot/config.txt`. The associated pins are listed below.
//!
//! * MISO: BCM GPIO 9 (physical pin 21)
//! * MOSI: BCM GPIO 10 (physical pin 19)
//! * SCLK: BCM GPIO 11 (physical pin 23)
//! * SS: CE0: BCM GPIO 8 (physical pin 24), CE1: BCM GPIO 7 (physical pin 26)
//!
//! SPI1 is an auxiliary peripheral that's referred to as mini SPI. According
//! to the documentation, using higher clock speeds on SPI1 requires additional
//! CPU time compared to SPI0, caused by shallow FIFOs and no DMA support. SPI1
//! can be enabled by adding `dtoverlay=spi1-3cs` to `/boot/config.txt`. Replace
//! `3cs` with either `2cs` or `1cs` if you only require 2 or 1 Slave Select pins.
//! The associated pins are listed below.
//!
//! * MISO: BCM GPIO 19 (physical pin 35)
//! * MOSI: BCM GPIO 20 (physical pin 38)
//! * SCLK: BCM GPIO 21 (physical pin 40)
//! * SS: CE0: BCM GPIO 18 (physical pin 12), CE1: BCM GPIO 17 (physical pin 11), CE2: BCM GPIO 16 (physical pin 36)
//!
//! SPI2 shares the same characteristics as SPI1. It can be enabled by adding
//! `dtoverlay=spi2-3cs` to `/boot/config.txt`. Replace `3cs` with either `2cs` or
//! `1cs` if you only require 2 or 1 Slave Select pins. The associated pins are
//! listed below.
//!
//! * MISO: BCM GPIO 40
//! * MOSI: BCM GPIO 41
//! * SCLK: BCM GPIO 42
//! * SS: CE0: BCM GPIO 43, CE1: BCM GPIO 44, CE2: BCM GPIO 45
//!
//! The GPIO pin numbers mentioned above are part of the default configuration. Some of
//! their functionality can be moved to different pins. Read `/boot/overlays/README`
//! for more information.

use std::fs::{File, OpenOptions};
use std::io;
use std::os::unix::io::AsRawFd;
use std::result;

use nix;

pub use nix::errno::Errno;

quick_error! {
    #[derive(Debug)]
/// Errors that can occur when accessing the SPI peripherals.
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// System call error.
        Sys(errno: Errno) { display("System call error: {}", errno) }
/// Invalid path.
        InvalidPath {}
/// Invalid Utf8.
        InvalidUtf8 {}
/// Unsupported operation.
        UnsupportedOperation {}
    }
}

impl From<nix::Error> for Error {
    fn from(err: nix::Error) -> Error {
        match err {
            nix::Error::Sys(errno) => Error::Sys(errno),
            nix::Error::InvalidPath => Error::InvalidPath,
            nix::Error::InvalidUtf8 => Error::InvalidUtf8,
            nix::Error::UnsupportedOperation => Error::UnsupportedOperation,
        }
    }
}

/// Result type returned from methods that can have `spi::Error`s.
pub type Result<T> = result::Result<T, Error>;

mod ioctl {
    const SPI_IOC_MAGIC: u8 = b'k';

    const SPI_IOC_TYPE_MESSAGE: u8 = 0;
    const SPI_IOC_TYPE_MODE: u8 = 1;
    const SPI_IOC_TYPE_LSB_FIRST: u8 = 2;
    const SPI_IOC_TYPE_BITS_PER_WORD: u8 = 3;
    const SPI_IOC_TYPE_MAX_SPEED_HZ: u8 = 4;

    #[derive(Debug, PartialEq, Copy, Clone)]
    #[repr(C)]
    pub struct SpiIocTransfer {
        tx_buf: u64,
        rx_buf: u64,
        len: u32,
        speed_hz: u32,
        delay_usecs: u16,
        bits_per_word: u8,
        cs_change: u8,
        tx_nbits: u8,
        rx_nbits: u8,
        pad: u16,
    }

    ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; SpiIocTransfer);
    ioctl!(write_int spi_write_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE);
    ioctl!(write_int spi_write_lsb_first with SPI_IOC_MAGIC, SPI_IOC_TYPE_LSB_FIRST);
    ioctl!(write_int spi_write_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_TYPE_BITS_PER_WORD);
    ioctl!(write_int spi_write_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_TYPE_MAX_SPEED_HZ);
}

/// SPI buses.
///
/// The Raspberry Pi supports up to three SPI buses, depending on the model and
/// your `/boot/config.txt` configuration. More information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Bus {
    Spi0 = 0,
    Spi1 = 1,
    Spi2 = 2,
}

/// Chip Enable (Slave Select) pins.
///
/// The Chip Enable pin is used to signal which device should
/// pay attention to the SPI bus. Chip Enable is more commonly
/// known as Slave Select or Chip Select.
///
/// The number of available Chip Enable pins for the selected SPI
/// bus depends on your `/boot/config.txt` configuration. More
/// information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ChipEnable {
    Ce0 = 0,
    Ce1 = 1,
    Ce2 = 2,
}

/// SPI modes.
///
/// Select the appropriate SPI mode for your device. Each mode
/// configures the clock polarity (CPOL) and clock phase (CPHA)
/// as shown below:
///
/// * Mode0: CPOL 0, CPHA 0
/// * Mode1: CPOL 0, CPHA 1
/// * Mode2: CPOL 1, CPHA 0
/// * Mode3: CPOL 1, CPHA 1
///
/// More information on clock polarity and phase can be found on [Wikipedia].
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Mode {
    Mode0 = 0,
    Mode1 = 1,
    Mode2 = 2,
    Mode3 = 3,
}

/// Bit order.
///
/// The bit order determines in what order data is shifted out and shifted in.
/// Select the bit order that's appropriate for the device you're communicating with.
///
/// `MsbFirst` will transfer the most-significant bit first. `LsbFirst` will transfer the
/// least-significant bit first.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BitOrder {
    MsbFirst = 0,
    LsbFirst = 1,
}

/// Provides access to the Raspberry Pi's SPI peripherals.
///
/// Before using `Spi`, make sure your Raspberry Pi has the necessary SPI buses and Chip Enable pins
/// enabled. More information can be found [here].
///
/// [here]: index.html
pub struct Spi {
    spidev: File,
}

impl Spi {
    /// Creates a new instance of `Spi`.
    ///
    /// `bus` and `chip_enable` specify the selected SPI bus and one of its associated Chip Enable pins.
    ///
    /// `clock_speed` defines the maximum clock speed in Hz. The SPI driver will automatically select
    /// the closest valid frequency.
    ///
    /// `mode` selects the clock polarity and phase.
    ///
    /// `bit_order` sets the order in which bits are shifted out and in to most-significant bit first or
    /// least-significant bit first.
    pub fn new(
        bus: Bus,
        chip_enable: ChipEnable,
        clock_speed: u32,
        mode: Mode,
        bit_order: BitOrder,
    ) -> Result<Spi> {
        // We don't ask for bits per word, because the driver only supports
        // 8 bits (or 9 bits in LoSSI mode). Changing the SS polarity from
        // active-low to active-high isn't supported either.
        // Based on https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
        // and https://www.kernel.org/doc/Documentation/spi/spidev.

        let spidev = OpenOptions::new()
            .read(true)
            .write(true)
            .open(format!("/dev/spidev{}.{}", bus as u8, chip_enable as u8))?;

        // Configure SPI bus through ioctl calls
        unsafe {
            ioctl::spi_write_mode(spidev.as_raw_fd(), mode as i32)?;
            ioctl::spi_write_max_speed_hz(spidev.as_raw_fd(), clock_speed as i32)?;
            ioctl::spi_write_bits_per_word(spidev.as_raw_fd(), 8)?;
            ioctl::spi_write_lsb_first(spidev.as_raw_fd(), bit_order as i32)?;
        }

        Ok(Spi { spidev })
    }

    /// Receives incoming data from the slave device and writes it to `buffer`.
    ///
    /// The SPI protocol doesn't indicate how much incoming data is waiting,
    /// so the number of bytes read is always equal to the length of `buffer`.
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<()> {
        Ok(())
    }

    /// Sends the outgoing data contained in `buffer` to the slave device.
    pub fn write(&mut self, buffer: &[u8]) -> Result<()> {
        Ok(())
    }

    /// Sends and receives data at the same time.
    ///
    /// SPI is a full-duplex protocol that shifts out bits to the slave device through MOSI
    /// while simultaneously reading the bits shifted in on the MISO line by the slave. `transfer`
    /// receives incoming data and writes it to `read_buffer`, and sends the outgoing data
    /// contained in `write_buffer`.
    ///
    /// Because reading and writing occur simultaneously, `transfer` only transfers
    /// as many bytes as the shortest of the two buffers contains.
    pub fn transfer(&mut self, read_buffer: &mut [u8], write_buffer: &[u8]) -> Result<()> {
        Ok(())
    }
}
<commit_msg>Add read/write/transfer, document TransferSegment<commit_after>// Copyright (c) 2017-2018 Rene van der Meer
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//! Interface for the SPI peripherals.
//!
//! RPPAL provides access to the available SPI peripherals by using the `/dev/spidevB.C`
//! devices, where B points to an SPI bus (0, 1, 2), and C to a Chip Enable pin (0, 1, 2).
//!
//! The Raspberry Pi's GPIO header exposes several SPI buses. SPI0 is available
//! on all Raspberry Pi models. SPI1 is available on models with a 40-pin
//! header. SPI2 is only available on the Compute and Compute 3.
//!
//! SPI0 is disabled by default. You can enable it by running
//! `sudo raspi-config`, or by manually adding `dtparam=spi=on` to
//! `/boot/config.txt`. The associated pins are listed below.
//!
//! * MISO: BCM GPIO 9 (physical pin 21)
//! * MOSI: BCM GPIO 10 (physical pin 19)
//! * SCLK: BCM GPIO 11 (physical pin 23)
//! * SS: CE0: BCM GPIO 8 (physical pin 24), CE1: BCM GPIO 7 (physical pin 26)
//!
//! SPI1 is an auxiliary peripheral that's referred to as mini SPI. According
//! to the documentation, using higher clock speeds on SPI1 requires additional
//! CPU time compared to SPI0, caused by shallow FIFOs and no DMA support. SPI1
//! can be enabled by adding `dtoverlay=spi1-3cs` to `/boot/config.txt`. Replace
//! `3cs` with either `2cs` or `1cs` if you only require 2 or 1 Slave Select pins.
//! The associated pins are listed below.
//!
//! * MISO: BCM GPIO 19 (physical pin 35)
//! * MOSI: BCM GPIO 20 (physical pin 38)
//! * SCLK: BCM GPIO 21 (physical pin 40)
//! * SS: CE0: BCM GPIO 18 (physical pin 12), CE1: BCM GPIO 17 (physical pin 11), CE2: BCM GPIO 16 (physical pin 36)
//!
//! SPI2 shares the same characteristics as SPI1. It can be enabled by adding
//! `dtoverlay=spi2-3cs` to `/boot/config.txt`. Replace `3cs` with either `2cs` or
//! `1cs` if you only require 2 or 1 Slave Select pins. The associated pins are
//! listed below.
//!
//! * MISO: BCM GPIO 40
//! * MOSI: BCM GPIO 41
//! * SCLK: BCM GPIO 42
//! * SS: CE0: BCM GPIO 43, CE1: BCM GPIO 44, CE2: BCM GPIO 45
//!
//! The GPIO pin numbers mentioned above are part of the default configuration. Some of
//! their functionality can be moved to different pins. Read `/boot/overlays/README`
//! for more information.

use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::result;

use nix;

pub use nix::errno::Errno;

quick_error! {
    #[derive(Debug)]
/// Errors that can occur when accessing the SPI peripherals.
    pub enum Error {
/// IO error.
        Io(err: io::Error) { description(err.description()) from() }
/// System call error.
        Sys(errno: Errno) { display("System call error: {}", errno) }
/// Invalid path.
        InvalidPath {}
/// Invalid Utf8.
        InvalidUtf8 {}
/// Unsupported operation.
        UnsupportedOperation {}
    }
}

impl From<nix::Error> for Error {
    fn from(err: nix::Error) -> Error {
        match err {
            nix::Error::Sys(errno) => Error::Sys(errno),
            nix::Error::InvalidPath => Error::InvalidPath,
            nix::Error::InvalidUtf8 => Error::InvalidUtf8,
            nix::Error::UnsupportedOperation => Error::UnsupportedOperation,
        }
    }
}

/// Result type returned from methods that can have `spi::Error`s.
pub type Result<T> = result::Result<T, Error>;

mod ioctl {
    const SPI_IOC_MAGIC: u8 = b'k';

    const SPI_IOC_TYPE_MESSAGE: u8 = 0;
    const SPI_IOC_TYPE_LSB_FIRST: u8 = 2;
    const SPI_IOC_TYPE_BITS_PER_WORD: u8 = 3;
    const SPI_IOC_TYPE_MAX_SPEED_HZ: u8 = 4;
    const SPI_IOC_TYPE_MODE32: u8 = 5;

    #[derive(Debug, PartialEq, Copy, Clone)]
    #[repr(C)]
    pub struct TransferSegment {
        // Pointer to transmit buffer, or 0.
        tx_buf: u64,
        // Pointer to receive buffer, or 0.
        rx_buf: u64,
        // Number of bytes to transfer in this segment.
        len: u32,
        // Set a different clock speed for this segment. 0 = default.
        speed_hz: u32,
        // Add a delay before the (optional) SS change and the next segment.
        delay_usecs: u16,
        // Not used, since we only support 8 bits. 0 = default.
        bits_per_word: u8,
        // Set to 1 to briefly set SS inactive between this segment and the next, and keep SS active after the final segment.
        cs_change: u8,
        // Used for dual/quad SPI.
        tx_nbits: u8,
        // Used for dual/quad SPI.
        rx_nbits: u8,
        // Padding. Set to 0 for forward compatibility.
        pad: u16,
    }

    // TODO: Doublecheck cs_change behavior after the final transfer segment. Some SPI drivers may have
    // interpreted the documentation incorrectly.

    impl TransferSegment {
        pub fn new(read_buffer: Option<&mut [u8]>, write_buffer: Option<&[u8]>) -> TransferSegment {
            // Len will contain the length of the shortest of the supplied buffers
            let mut len: u32 = 0;

            let tx_buf = if let Some(buffer) = write_buffer {
                len = buffer.len() as u32;
                buffer.as_ptr() as u64
            } else {
                0
            };

            let rx_buf = if let Some(buffer) = read_buffer {
                if len > buffer.len() as u32 {
                    len = buffer.len() as u32;
                }
                buffer.as_ptr() as u64
            } else {
                0
            };

            TransferSegment {
                tx_buf,
                rx_buf,
                len,
                speed_hz: 0,
                delay_usecs: 0,
                bits_per_word: 0,
                cs_change: 0,
                tx_nbits: 0,
                rx_nbits: 0,
                pad: 0,
            }
        }

        pub fn len(&self) -> u32 {
            self.len
        }
    }

    ioctl!(write_buf spi_transfer with SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE; TransferSegment);
    ioctl!(write_int spi_write_mode with SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE32);
    ioctl!(write_int spi_write_lsb_first with SPI_IOC_MAGIC, SPI_IOC_TYPE_LSB_FIRST);
    ioctl!(write_int spi_write_bits_per_word with SPI_IOC_MAGIC, SPI_IOC_TYPE_BITS_PER_WORD);
    ioctl!(write_int spi_write_max_speed_hz with SPI_IOC_MAGIC, SPI_IOC_TYPE_MAX_SPEED_HZ);
}

/// SPI buses.
///
/// The Raspberry Pi supports up to three SPI buses, depending on the model and
/// your `/boot/config.txt` configuration. More information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Bus {
    Spi0 = 0,
    Spi1 = 1,
    Spi2 = 2,
}

/// Chip Enable (Slave Select) pins.
///
/// The Chip Enable pin is used to signal which device should
/// pay attention to the SPI bus. Chip Enable is more commonly
/// known as Slave Select or Chip Select.
///
/// The number of available Chip Enable pins for the selected SPI
/// bus depends on your `/boot/config.txt` configuration. More
/// information can be found [here].
///
/// [here]: index.html
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum ChipEnable {
    Ce0 = 0,
    Ce1 = 1,
    Ce2 = 2,
}

/// SPI modes.
///
/// Select the appropriate SPI mode for your device. Each mode
/// configures the clock polarity (CPOL) and clock phase (CPHA)
/// as shown below:
///
/// * Mode0: CPOL 0, CPHA 0
/// * Mode1: CPOL 0, CPHA 1
/// * Mode2: CPOL 1, CPHA 0
/// * Mode3: CPOL 1, CPHA 1
///
/// More information on clock polarity and phase can be found on [Wikipedia].
///
/// [Wikipedia]: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Clock_polarity_and_phase
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Mode {
    Mode0 = 0,
    Mode1 = 1,
    Mode2 = 2,
    Mode3 = 3,
}

/// Bit order.
///
/// The bit order determines in what order data is shifted out and shifted in.
/// Select the bit order that's appropriate for the device you're communicating with.
///
/// `MsbFirst` will transfer the most-significant bit first. `LsbFirst` will transfer the
/// least-significant bit first.
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum BitOrder {
    MsbFirst = 0,
    LsbFirst = 1,
}

/// Provides access to the Raspberry Pi's SPI peripherals.
///
/// Before using `Spi`, make sure your Raspberry Pi has the necessary SPI buses and Chip Enable pins
/// enabled. More information can be found [here].
///
/// [here]: index.html
pub struct Spi {
    spidev: File,
}

impl Spi {
    /// Creates a new instance of `Spi`.
    ///
    /// `bus` and `chip_enable` specify the selected SPI bus and one of its associated Chip Enable pins.
    ///
    /// `clock_speed` defines the maximum clock speed in Hz. The SPI driver will automatically select
    /// the closest valid frequency.
    ///
    /// `mode` selects the clock polarity and phase.
    ///
    /// `bit_order` sets the order in which bits are shifted out and in to most-significant bit first or
    /// least-significant bit first.
    pub fn new(
        bus: Bus,
        chip_enable: ChipEnable,
        clock_speed: u32,
        mode: Mode,
        bit_order: BitOrder,
    ) -> Result<Spi> {
        // We don't ask for bits per word, because the driver only supports
        // 8 bits (or 9 bits in LoSSI mode). Changing the SS polarity from
        // active-low to active-high isn't supported either.
        // Based on https://www.raspberrypi.org/documentation/hardware/raspberrypi/spi/README.md
        // and https://www.kernel.org/doc/Documentation/spi/spidev.

        let spidev = OpenOptions::new()
            .read(true)
            .write(true)
            .open(format!("/dev/spidev{}.{}", bus as u8, chip_enable as u8))?;

        // Configure SPI bus through ioctl calls
        unsafe {
            ioctl::spi_write_mode(spidev.as_raw_fd(), mode as i32)?;
            ioctl::spi_write_max_speed_hz(spidev.as_raw_fd(), clock_speed as i32)?;
            ioctl::spi_write_bits_per_word(spidev.as_raw_fd(), 8)?;
            ioctl::spi_write_lsb_first(spidev.as_raw_fd(), bit_order as i32)?;
        }

        Ok(Spi { spidev })
    }

    /// Receives incoming data from the slave device and writes it to `buffer`.
    ///
    /// The SPI protocol doesn't indicate how much incoming data is waiting,
    /// so the maximum number of bytes read depends on the length of `buffer`.
    ///
    /// During the read, the MOSI line is kept in a state that results in a zero
    /// value byte shifted out for every byte `read` receives on the MISO line.
    ///
    /// Chip Enable is set to Low (active) at the start of the read, and
    /// High (inactive) when the read completes.
    ///
    /// Returns how many bytes were read.
    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
        Ok(self.spidev.read(buffer)?)
    }

    /// Sends the outgoing data contained in `buffer` to the slave device.
    ///
    /// Any data received on the MISO line from the slave is ignored.
    ///
    /// Chip Enable is set to Low (active) at the start of the write, and
    /// High (inactive) when the write completes.
    ///
    /// Returns how many bytes were written.
    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
        Ok(self.spidev.write(buffer)?)
    }

    /// Sends and receives data at the same time.
    ///
    /// SPI is a full-duplex protocol that shifts out bits to the slave device on the MOSI
    /// line while simultaneously shifting in bits it receives on the MISO line.
    /// `transfer` stores the incoming data in `read_buffer`, and sends the outgoing data
    /// contained in `write_buffer`.
    ///
    /// Because data is sent and received simultaneously, `transfer` only transfers
    /// as many bytes as the shortest of the two buffers contains.
    ///
    /// Chip Enable is set to Low (active) at the start of the transfer, and
    /// High (inactive) when the transfer completes.
    ///
    /// Returns how many bytes were transferred.
    pub fn transfer(&mut self, read_buffer: &mut [u8], write_buffer: &[u8]) -> Result<usize> {
        let segment = ioctl::TransferSegment::new(Some(read_buffer), Some(write_buffer));

        unsafe {
            ioctl::spi_transfer(self.spidev.as_raw_fd(), &[segment])?;
        }

        Ok(segment.len() as usize)
    }
}
<|endoftext|>


<commit_before>//! General implementation of tdos base structure.
use json::parse;
use std::fs::File;
use std::io::Read;
use list::TodoList;
use legacy::*;
use todo::Todo;
use error::*;

/// Basic container structure for a set of todo lists.
///
/// This data structure acts as a conatiner for all todo lists and its associated todos.
/// The whole `tdo` microcosm settles around this structure
/// which is also used for (de-)serialization.
///
/// When instanciated, it comes with an empty _default_ list.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tdo {
    /// A vector of all todo lists.
    pub lists: Vec<TodoList>,
    //The Github API token.
    access_token: Option<String>,
    // The tdo version the last dump was saved with.
    version: String,
}

impl Tdo {
    /// Create a new `Tdo` container.
    /// Each new container is instanciated with a _default_ `TodoList`.
    ///
    /// # Example
    ///
    /// ```
    /// # use tdo_core::tdo::*;
    /// let tdo = Tdo::new();
    /// ```
    pub fn new() -> Tdo {
        Tdo {
            lists: vec![TodoList::default()],
            access_token: None,
            version: env!("CARGO_PKG_VERSION").to_string(),
        }
    }

    /// Load a saved `Tdo` container from a JSON file.
    ///
    /// This function returns a `ResultType` which will yield the
    /// deserialized JSON or a `serde_json::Error`.
    ///
    /// # Example
    ///
    /// ```
    /// # use tdo_core::tdo::*;
    /// let mut tdo = Tdo::load("foo.json");
    /// ```
    pub fn load(path: &str) -> TdoResult<Tdo> {
        match File::open(path) {
            Ok(file) => {
                match super::serde_json::from_reader(&file) {
                    Ok(tdo) => Ok(tdo),
                    Err(_) => update_json(path),
                }
            }
            Err(_) => Err(ErrorKind::StorageError(storage_error::ErrorKind::FileNotFound).into()),
        }

    }

    /// Dump the `Tdo` container to a JSON file.
    ///
    /// This function returns a `ResultType` yielding a `StorageError::SaveFailure`
    /// if the JSON file could not be opened/saved.
    ///
    /// # Example
    ///
    /// ```
    /// # use tdo_core::tdo::*;
    /// # let mut tdo = Tdo::new();
    /// let res = tdo.save("foo.json");
    /// assert_eq!(res.unwrap(), ());
    /// ```
    pub fn save(&self, path: &str) -> TdoResult<()> {
        // TODO: At this point we could be much more precise about the error if we would include
        // the error from the file system as SaveFailure(ArbitraryErrorFromFS)
        //  -- Feliix42 (2017-03-14; 17:04)
        match File::create(path) {
            Ok(mut f) => {
                let _ = super::serde_json::to_writer_pretty(&mut f, self);
                Ok(())
            }
            Err(_) => Err(ErrorKind::StorageError(storage_error::ErrorKind::SaveFailure).into()),
        }
    }

    /// Sets the GitHub access token.
    pub fn set_gh_token(&mut self, token: &str) {
        self.access_token = Some(token.to_string());
    }

    /// Returns an Option<String> of the private access_token field.
    pub fn get_gh_token(&self) -> Option<String> {
        self.access_token.to_owned()
    }

    /// Add a todo list to the container.
    pub fn add_list(&mut self, list: TodoList) -> TdoResult<()> {
        match self.get_list_index(&list.name) {
            Ok(_) => Err(ErrorKind::TodoError(todo_error::ErrorKind::NameAlreadyExists).into()),
            Err(_) => {
                self.lists.push(list);
                Ok(())
            }
        }
    }

    /// Removes a list from the container.
    pub fn remove_list(&mut self, list_name: &str) -> TdoResult<()> {
        if list_name == "default" {
            Err(ErrorKind::TodoError(todo_error::ErrorKind::CanNotRemoveDefault).into())
        } else {
            match self.get_list_index(list_name) {
                Ok(index) => {
                    self.lists.remove(index);
                    Ok(())
                }
                Err(_) => Err(ErrorKind::TodoError(todo_error::ErrorKind::NoSuchList).into()),
            }
        }
    }

    /// Add a todo to the todo list, identified by its name.
    ///
    /// This function returns a `ResultType` with a `TodoError::NoSuchList`
    /// if there is no matching list found.
    pub fn add_todo(&mut self, list_name: Option<&str>, todo: Todo) -> TdoResult<()> {
        match self.get_list_index(&list_name.unwrap_or("default")) {
            Ok(index) => {
                self.lists[index].add(todo);
                Ok(())
            }
            Err(x) => Err(x),
        }
    }

    /// Cycle through all todo lists and find the list which contains the todo with the given ID
    ///
    /// This function retuns a `ResultType` with a `TodoError::NotInList`
    /// if there is no list found or a usize with the postition of the list in lists.
    pub fn find_id(&self, id: u32) -> TdoResult<usize> {
        for list in 0..self.lists.len() {
            if self.lists[list].contains_id(id).is_ok() {
                return Ok(list);
            }
        }
        Err(ErrorKind::TodoError(todo_error::ErrorKind::NotInList).into())
    }
    /// Cycle through all todo lists and mark a todo with the given ID as done.
    /// This function has no return value and thus won't indicate whether
    /// there was a matching todo found.
    pub fn done_id(&mut self, id: u32) -> TdoResult<()> {
        let list = match self.find_id(id) {
            Ok(list_id) => list_id,
            Err(e) => return Err(e),
        };
        self.lists[list].done_id(id)
    }

    /// Cycle through all todo lists and remove a todo with the given id.
    /// This function has no return value and thus won't indicate whether
    /// there was a matching todo found.
    pub fn remove_id(&mut self, id: u32) -> TdoResult<()> {
        let list = match self.find_id(id) {
            Ok(list_id) => list_id,
            Err(e) => return Err(e),
        };
        match self.lists[list].remove_id(id) {
            Err(e) => Err(e),
            _ => Ok(()),
        }
    }

    /// Remove all todos that have been marked as _done_ from all todo lists.
    pub fn clean_lists(&mut self) {
        for list in 0..self.lists.len() {
            self.lists[list].clean();
        }
    }

    /// Remove all todos that have been marked as _done_ from a given todo list.
    pub fn clean_list(&mut self, list: &str) -> TdoResult<()> {
        let index = match self.get_list_index(list) {
            Ok(index) => index,
            Err(e) => return Err(e),
        };
        self.lists[index].clean();
        Ok(())
    }

    fn get_list_index(&self, name: &str) -> TdoResult<usize> {
        match self.lists
            .iter()
            .position(|x| x.name.to_lowercase() == name.to_string().to_lowercase()) {
            Some(index) => Ok(index),
            None => Err(ErrorKind::TodoError(todo_error::ErrorKind::NoSuchList).into()),
        }
    }

    /// Get the highest ID used in the tdo container.
    pub fn get_highest_id(&self) -> u32 {
        self.lists
            .iter()
            .fold(0, |acc, &ref x| {
                x.list
                    .iter()
                    .fold(acc,
                          |inner_acc, &ref y| if inner_acc < y.id { y.id } else { inner_acc })
            })
    }

    /// Move a `todo` between two lists.
    pub fn move_todo(&mut self, id: u32, target_list: &str) -> TdoResult<()> {
        let src_index = self.find_id(id)?;
        let target = self.get_list_index(target_list)?;

        let todo = self.lists[src_index].pop_id(id)?;
        self.lists[target].insert_todo(todo);
        Ok(())
    }
}

fn update_json(path: &str) -> TdoResult<Tdo> {
    match Tdo01::load(path) {
        Ok(tdo) => Ok(tdo.into()),
        Err(_) => {
            println!("I have to do this here");
            let mut file = File::open(path).unwrap();
            let mut data = String::new();
            file.read_to_string(&mut data).unwrap();
            let mut json = match parse(&data) {
                Ok(content) => content,
                Err(_) => {
                    return Err(ErrorKind::StorageError(storage_error::ErrorKind::FileCorrupted)
                        .into())
                }
            };

            let mut lists: Vec<TodoList> = vec![];

            for outer in json.entries_mut() {
                let mut list = TodoList::new(outer.0);
                for inner in outer.1.entries_mut() {
                    let tdo_id = match inner.0.parse::<u32>() {
                        Ok(id) => id,
                        Err(_) => return Err(ErrorKind::StorageError(storage_error::ErrorKind::UnableToConvert).into()),
                    };
                    let done = match inner.1.pop().as_bool() {
                        Some(x) => x,
                        None => return Err(ErrorKind::StorageError(storage_error::ErrorKind::UnableToConvert).into()),
                    };
                    let tdo_name = match inner.1.pop().as_str() {
                        Some(x) => String::from(x),
                        None => return Err(ErrorKind::StorageError(storage_error::ErrorKind::UnableToConvert).into()),
                    };
                    let mut todo = Todo::new(tdo_id, &tdo_name);
                    if done {
                        todo.set_done();
                    }
                    list.add(todo);
                }
                lists.push(list);
            }
            let tdo = Tdo {
                lists: lists,
                access_token: None,
                version: env!("CARGO_PKG_VERSION").to_string(),
            };
            Ok(tdo)
        }
    }
}
<commit_msg>edit setting the github access token<commit_after>//! General implementation of tdos base structure.
use json::parse;
use std::fs::File;
use std::io::{Read, Write, stdout, stdin};
use list::TodoList;
use legacy::*;
use todo::Todo;
use error::*;

/// Basic container structure for a set of todo lists.
///
/// This data structure acts as a conatiner for all todo lists and its associated todos.
/// The whole `tdo` microcosm settles around this structure
/// which is also used for (de-)serialization.
///
/// When instanciated, it comes with an empty _default_ list.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Tdo {
    /// A vector of all todo lists.
    pub lists: Vec<TodoList>,
    //The Github API token.
    access_token: Option<String>,
    // The tdo version the last dump was saved with.
    version: String,
}

impl Tdo {
    /// Create a new `Tdo` container.
    /// Each new container is instanciated with a _default_ `TodoList`.
    ///
    /// # Example
    ///
    /// ```
    /// # use tdo_core::tdo::*;
    /// let tdo = Tdo::new();
    /// ```
    pub fn new() -> Tdo {
        Tdo {
            lists: vec![TodoList::default()],
            access_token: None,
            version: env!("CARGO_PKG_VERSION").to_string(),
        }
    }

    /// Load a saved `Tdo` container from a JSON file.
    ///
    /// This function returns a `ResultType` which will yield the
    /// deserialized JSON or a `serde_json::Error`.
    ///
    /// # Example
    ///
    /// ```
    /// # use tdo_core::tdo::*;
    /// let mut tdo = Tdo::load("foo.json");
    /// ```
    pub fn load(path: &str) -> TdoResult<Tdo> {
        match File::open(path) {
            Ok(file) => {
                match super::serde_json::from_reader(&file) {
                    Ok(tdo) => Ok(tdo),
                    Err(_) => update_json(path),
                }
            }
            Err(_) => Err(ErrorKind::StorageError(storage_error::ErrorKind::FileNotFound).into()),
        }

    }

    /// Dump the `Tdo` container to a JSON file.
    ///
    /// This function returns a `ResultType` yielding a `StorageError::SaveFailure`
    /// if the JSON file could not be opened/saved.
    ///
    /// # Example
    ///
    /// ```
    /// # use tdo_core::tdo::*;
    /// # let mut tdo = Tdo::new();
    /// let res = tdo.save("foo.json");
    /// assert_eq!(res.unwrap(), ());
    /// ```
    pub fn save(&self, path: &str) -> TdoResult<()> {
        // TODO: At this point we could be much more precise about the error if we would include
        // the error from the file system as SaveFailure(ArbitraryErrorFromFS)
        //  -- Feliix42 (2017-03-14; 17:04)
        match File::create(path) {
            Ok(mut f) => {
                let _ = super::serde_json::to_writer_pretty(&mut f, self);
                Ok(())
            }
            Err(_) => Err(ErrorKind::StorageError(storage_error::ErrorKind::SaveFailure).into()),
        }
    }

    /// Sets the GitHub access token.
    pub fn set_gh_token(&mut self, token: Option<&str>) {
        let gh_token = match token {
            Some(x) => x.to_string(),
            None => {
                print!("Please generate an access token ({})\nand enter a valid accesstoken: ",
                       "https://github.com/settings/tokens/new?scopes=repo&description=tdolist");
                stdout().flush().ok().expect("Could not flush stdout!");
                let mut answer = String::new();
                stdin().read_line(&mut answer).unwrap();
                answer.trim().to_string()
            }
        };
        self.access_token = Some(gh_token);
    }

    /// Returns an Option<String> of the private access_token field.
    pub fn get_gh_token(&self) -> Option<String> {
        self.access_token.to_owned()
    }

    /// Add a todo list to the container.
    pub fn add_list(&mut self, list: TodoList) -> TdoResult<()> {
        match self.get_list_index(&list.name) {
            Ok(_) => Err(ErrorKind::TodoError(todo_error::ErrorKind::NameAlreadyExists).into()),
            Err(_) => {
                self.lists.push(list);
                Ok(())
            }
        }
    }

    /// Removes a list from the container.
    pub fn remove_list(&mut self, list_name: &str) -> TdoResult<()> {
        if list_name == "default" {
            Err(ErrorKind::TodoError(todo_error::ErrorKind::CanNotRemoveDefault).into())
        } else {
            match self.get_list_index(list_name) {
                Ok(index) => {
                    self.lists.remove(index);
                    Ok(())
                }
                Err(_) => Err(ErrorKind::TodoError(todo_error::ErrorKind::NoSuchList).into()),
            }
        }
    }

    /// Add a todo to the todo list, identified by its name.
    ///
    /// This function returns a `ResultType` with a `TodoError::NoSuchList`
    /// if there is no matching list found.
    pub fn add_todo(&mut self, list_name: Option<&str>, todo: Todo) -> TdoResult<()> {
        match self.get_list_index(&list_name.unwrap_or("default")) {
            Ok(index) => {
                self.lists[index].add(todo);
                Ok(())
            }
            Err(x) => Err(x),
        }
    }

    /// Cycle through all todo lists and find the list which contains the todo with the given ID
    ///
    /// This function retuns a `ResultType` with a `TodoError::NotInList`
    /// if there is no list found or a usize with the postition of the list in lists.
    pub fn find_id(&self, id: u32) -> TdoResult<usize> {
        for list in 0..self.lists.len() {
            if self.lists[list].contains_id(id).is_ok() {
                return Ok(list);
            }
        }
        Err(ErrorKind::TodoError(todo_error::ErrorKind::NotInList).into())
    }
    /// Cycle through all todo lists and mark a todo with the given ID as done.
    /// This function has no return value and thus won't indicate whether
    /// there was a matching todo found.
    pub fn done_id(&mut self, id: u32) -> TdoResult<()> {
        let list = match self.find_id(id) {
            Ok(list_id) => list_id,
            Err(e) => return Err(e),
        };
        self.lists[list].done_id(id)
    }

    /// Cycle through all todo lists and remove a todo with the given id.
    /// This function has no return value and thus won't indicate whether
    /// there was a matching todo found.
    pub fn remove_id(&mut self, id: u32) -> TdoResult<()> {
        let list = match self.find_id(id) {
            Ok(list_id) => list_id,
            Err(e) => return Err(e),
        };
        match self.lists[list].remove_id(id) {
            Err(e) => Err(e),
            _ => Ok(()),
        }
    }

    /// Remove all todos that have been marked as _done_ from all todo lists.
    pub fn clean_lists(&mut self) {
        for list in 0..self.lists.len() {
            self.lists[list].clean();
        }
    }

    /// Remove all todos that have been marked as _done_ from a given todo list.
    pub fn clean_list(&mut self, list: &str) -> TdoResult<()> {
        let index = match self.get_list_index(list) {
            Ok(index) => index,
            Err(e) => return Err(e),
        };
        self.lists[index].clean();
        Ok(())
    }

    fn get_list_index(&self, name: &str) -> TdoResult<usize> {
        match self.lists
            .iter()
            .position(|x| x.name.to_lowercase() == name.to_string().to_lowercase()) {
            Some(index) => Ok(index),
            None => Err(ErrorKind::TodoError(todo_error::ErrorKind::NoSuchList).into()),
        }
    }

    /// Get the highest ID used in the tdo container.
    pub fn get_highest_id(&self) -> u32 {
        self.lists
            .iter()
            .fold(0, |acc, &ref x| {
                x.list
                    .iter()
                    .fold(acc,
                          |inner_acc, &ref y| if inner_acc < y.id { y.id } else { inner_acc })
            })
    }

    /// Move a `todo` between two lists.
    pub fn move_todo(&mut self, id: u32, target_list: &str) -> TdoResult<()> {
        let src_index = self.find_id(id)?;
        let target = self.get_list_index(target_list)?;

        let todo = self.lists[src_index].pop_id(id)?;
        self.lists[target].insert_todo(todo);
        Ok(())
    }
}

fn update_json(path: &str) -> TdoResult<Tdo> {
    match Tdo01::load(path) {
        Ok(tdo) => Ok(tdo.into()),
        Err(_) => {
            println!("I have to do this here");
            let mut file = File::open(path).unwrap();
            let mut data = String::new();
            file.read_to_string(&mut data).unwrap();
            let mut json = match parse(&data) {
                Ok(content) => content,
                Err(_) => {
                    return Err(ErrorKind::StorageError(storage_error::ErrorKind::FileCorrupted)
                        .into())
                }
            };

            let mut lists: Vec<TodoList> = vec![];

            for outer in json.entries_mut() {
                let mut list = TodoList::new(outer.0);
                for inner in outer.1.entries_mut() {
                    let tdo_id = match inner.0.parse::<u32>() {
                        Ok(id) => id,
                        Err(_) => return Err(ErrorKind::StorageError(storage_error::ErrorKind::UnableToConvert).into()),
                    };
                    let done = match inner.1.pop().as_bool() {
                        Some(x) => x,
                        None => return Err(ErrorKind::StorageError(storage_error::ErrorKind::UnableToConvert).into()),
                    };
                    let tdo_name = match inner.1.pop().as_str() {
                        Some(x) => String::from(x),
                        None => return Err(ErrorKind::StorageError(storage_error::ErrorKind::UnableToConvert).into()),
                    };
                    let mut todo = Todo::new(tdo_id, &tdo_name);
                    if done {
                        todo.set_done();
                    }
                    list.add(todo);
                }
                lists.push(list);
            }
            let tdo = Tdo {
                lists: lists,
                access_token: None,
                version: env!("CARGO_PKG_VERSION").to_string(),
            };
            Ok(tdo)
        }
    }
}
<|endoftext|>


<commit_before>//! Components needed to verify TUF metadata and targets.

use chrono::offset::Utc;
use std::marker::PhantomData;

use Result;
use crypto::KeyId;
use error::Error;
use interchange::DataInterchange;
use metadata::{SignedMetadata, RootMetadata, VerificationStatus, TimestampMetadata, Role,
               SnapshotMetadata, MetadataPath, TargetsMetadata};

/// Contains trusted TUF metadata and can be used to verify other metadata and targets.
#[derive(Debug)]
pub struct Tuf<D: DataInterchange> {
    root: RootMetadata,
    snapshot: Option<SnapshotMetadata>,
    targets: Option<TargetsMetadata>,
    timestamp: Option<TimestampMetadata>,
    _interchange: PhantomData<D>,
}

impl<D: DataInterchange> Tuf<D> {
    /// Create a new `TUF` struct from a known set of pinned root keys that are used to verify the
    /// signed metadata.
    pub fn from_root_pinned<V>(
        mut signed_root: SignedMetadata<D, RootMetadata, V>,
        root_key_ids: &[KeyId],
    ) -> Result<Self>
    where
        V: VerificationStatus,
    {
        signed_root.signatures_mut().retain(|s| {
            root_key_ids.contains(s.key_id())
        });
        Self::from_root(signed_root)
    }

    /// Create a new `TUF` struct from a piece of metadata that is assumed to be trusted.
    ///
    /// *WARNING*: This is trust-on-first-use (TOFU) and offers weaker security guarantees than the
    /// related method `from_root_pinned`.
    pub fn from_root<V>(signed_root: SignedMetadata<D, RootMetadata, V>) -> Result<Self>
    where
        V: VerificationStatus,
    {
        let root = D::deserialize::<RootMetadata>(signed_root.unverified_signed())?;
        let _ = signed_root.verify(
            root.root().threshold(),
            root.root().key_ids(),
            root.keys(),
        )?;
        Ok(Tuf {
            root: root,
            snapshot: None,
            targets: None,
            timestamp: None,
            _interchange: PhantomData,
        })
    }

    /// An immutable reference to the root metadata.
    pub fn root(&self) -> &RootMetadata {
        &self.root
    }

    /// An immutable reference to the optional snapshot metadata.
    pub fn snapshot(&self) -> Option<&SnapshotMetadata> {
        self.snapshot.as_ref()
    }

    /// An immutable reference to the optional targets metadata.
    pub fn targets(&self) -> Option<&TargetsMetadata> {
        self.targets.as_ref()
    }

    /// An immutable reference to the optional timestamp metadata.
    pub fn timestamp(&self) -> Option<&TimestampMetadata> {
        self.timestamp.as_ref()
    }

    /// Verify and update the root metadata.
    pub fn update_root<V>(
        &mut self,
        signed_root: SignedMetadata<D, RootMetadata, V>,
    ) -> Result<bool>
    where
        V: VerificationStatus,
    {
        let signed_root = signed_root.verify(
            self.root.root().threshold(),
            self.root.root().key_ids(),
            self.root.keys(),
        )?;

        let root = D::deserialize::<RootMetadata>(signed_root.unverified_signed())?;

        match root.version() {
            x if x == self.root.version() => {
                info!(
                    "Attempted to update root to new metadata with the same version. \
                      Refusing to update."
                );
                return Ok(false);
            }
            x if x < self.root.version() => {
                return Err(Error::VerificationFailure(format!(
                    "Attempted to roll back root metadata at version {} to {}.",
                    self.root.version(),
                    x
                )))
            }
            _ => (),
        }

        let _ = signed_root.verify(
            root.root().threshold(),
            root.root().key_ids(),
            root.keys(),
        )?;

        self.purge_metadata();

        self.root = root;
        Ok(true)
    }

    /// Verify and update the timestamp metadata.
    pub fn update_timestamp<V>(
        &mut self,
        signed_timestamp: SignedMetadata<D, TimestampMetadata, V>,
    ) -> Result<bool>
    where
        V: VerificationStatus,
    {
        let signed_timestamp = signed_timestamp.verify(
            self.root.timestamp().threshold(),
            self.root.timestamp().key_ids(),
            self.root.keys(),
        )?;

        let current_version = self.timestamp.as_ref().map(|t| t.version()).unwrap_or(0);
        let timestamp: TimestampMetadata = D::deserialize(&signed_timestamp.signed())?;

        if timestamp.expires() <= &Utc::now() {
            return Err(Error::ExpiredMetadata(Role::Timestamp));
        }

        if timestamp.version() < current_version {
            Err(Error::VerificationFailure(format!(
                "Attempted to roll back timestamp metadata at version {} to {}.",
                current_version,
                timestamp.version()
            )))
        } else if timestamp.version() == current_version {
            Ok(false)
        } else {
            self.timestamp = Some(timestamp);
            Ok(true)
        }
    }

    /// Verify and update the snapshot metadata.
    pub fn update_snapshot<V>(
        &mut self,
        signed_snapshot: SignedMetadata<D, SnapshotMetadata, V>,
    ) -> Result<bool>
    where
        V: VerificationStatus,
    {
        let snapshot = {
            let root = self.safe_root_ref()?;
            let timestamp = self.safe_timestamp_ref()?;
            let snapshot_description = timestamp
                .meta()
                .get(&MetadataPath::from_role(&Role::Snapshot))
                .ok_or_else(|| {
                    Error::VerificationFailure(
                        "Timestamp metadata had no description of the snapshot metadata".into(),
                    )
                })?;

            let signed_snapshot = signed_snapshot.verify(
                root.snapshot().threshold(),
                root.snapshot().key_ids(),
                root.keys(),
            )?;

            let current_version = self.snapshot.as_ref().map(|t| t.version()).unwrap_or(0);
            let snapshot: SnapshotMetadata = D::deserialize(&signed_snapshot.signed())?;

            if snapshot.version() != snapshot_description.version() {
                return Err(Error::VerificationFailure(format!(
                    "The timestamp metadata reported that the snapshot metadata should be at \
                    version {} but version {} was found instead.",
                    snapshot_description.version(),
                    snapshot.version()
                )));
            }

            if snapshot.expires() <= &Utc::now() {
                return Err(Error::ExpiredMetadata(Role::Snapshot));
            }

            if snapshot.version() < current_version {
                return Err(Error::VerificationFailure(format!(
                    "Attempted to roll back snapshot metadata at version {} to {}.",
                    current_version,
                    snapshot.version()
                )));
            } else if snapshot.version() == current_version {
                return Ok(false);
            } else {
                snapshot
            }
        };

        self.snapshot = Some(snapshot);
        Ok(true)
    }

    /// Verify and update the targets metadata.
    pub fn update_targets<V>(
        &mut self,
        signed_targets: SignedMetadata<D, TargetsMetadata, V>,
    ) -> Result<bool>
    where
        V: VerificationStatus,
    {
        let targets = {
            let root = self.safe_root_ref()?;
            let snapshot = self.safe_snapshot_ref()?;
            let targets_description = snapshot
                .meta()
                .get(&MetadataPath::from_role(&Role::Targets))
                .ok_or_else(|| {
                    Error::VerificationFailure(
                        "Snapshot metadata had no description of the targets metadata".into(),
                    )
                })?;

            let signed_targets = signed_targets.verify(
                root.targets().threshold(),
                root.targets().key_ids(),
                root.keys(),
            )?;

            let current_version = self.targets.as_ref().map(|t| t.version()).unwrap_or(0);
            let targets: TargetsMetadata = D::deserialize(&signed_targets.signed())?;

            if targets.version() != targets_description.version() {
                return Err(Error::VerificationFailure(format!(
                    "The timestamp metadata reported that the targets metadata should be at \
                    version {} but version {} was found instead.",
                    targets_description.version(),
                    targets.version()
                )));
            }

            if targets.expires() <= &Utc::now() {
                return Err(Error::ExpiredMetadata(Role::Snapshot));
            }

            if targets.version() < current_version {
                return Err(Error::VerificationFailure(format!(
                    "Attempted to roll back targets metadata at version {} to {}.",
                    current_version,
                    targets.version()
                )));
            } else if targets.version() == current_version {
                return Ok(false);
            } else {
                targets
            }
        };

        self.targets = Some(targets);
        Ok(true)
    }

    fn purge_metadata(&mut self) {
        self.snapshot = None;
        self.targets = None;
        self.timestamp = None;
        // TODO include delegations
    }

    fn safe_root_ref(&self) -> Result<&RootMetadata> {
        if self.root.expires() <= &Utc::now() {
            return Err(Error::ExpiredMetadata(Role::Root));
        }
        Ok(&self.root)
    }

    fn safe_snapshot_ref(&self) -> Result<&SnapshotMetadata> {
        match &self.snapshot {
            &Some(ref snapshot) => {
                if snapshot.expires() <= &Utc::now() {
                    return Err(Error::ExpiredMetadata(Role::Snapshot));
                }
                Ok(snapshot)
            }
            &None => Err(Error::MissingMetadata(Role::Snapshot)),
        }
    }

    fn safe_targets_ref(&self) -> Result<&TargetsMetadata> {
        match &self.targets {
            &Some(ref targets) => {
                if targets.expires() <= &Utc::now() {
                    return Err(Error::ExpiredMetadata(Role::Targets));
                }
                Ok(targets)
            }
            &None => Err(Error::MissingMetadata(Role::Targets)),
        }
    }
    fn safe_timestamp_ref(&self) -> Result<&TimestampMetadata> {
        match &self.timestamp {
            &Some(ref timestamp) => {
                if timestamp.expires() <= &Utc::now() {
                    return Err(Error::ExpiredMetadata(Role::Timestamp));
                }
                Ok(timestamp)
            }
            &None => Err(Error::MissingMetadata(Role::Timestamp)),
        }
    }
}
<commit_msg>list targets function<commit_after>//! Components needed to verify TUF metadata and targets.

use chrono::offset::Utc;
use std::collections::HashSet;
use std::marker::PhantomData;

use Result;
use crypto::KeyId;
use error::Error;
use interchange::DataInterchange;
use metadata::{SignedMetadata, RootMetadata, VerificationStatus, TimestampMetadata, Role,
               SnapshotMetadata, MetadataPath, TargetsMetadata, TargetPath};

/// Contains trusted TUF metadata and can be used to verify other metadata and targets.
#[derive(Debug)]
pub struct Tuf<D: DataInterchange> {
    root: RootMetadata,
    snapshot: Option<SnapshotMetadata>,
    targets: Option<TargetsMetadata>,
    timestamp: Option<TimestampMetadata>,
    _interchange: PhantomData<D>,
}

impl<D: DataInterchange> Tuf<D> {
    /// Create a new `TUF` struct from a known set of pinned root keys that are used to verify the
    /// signed metadata.
    pub fn from_root_pinned<V>(
        mut signed_root: SignedMetadata<D, RootMetadata, V>,
        root_key_ids: &[KeyId],
    ) -> Result<Self>
    where
        V: VerificationStatus,
    {
        signed_root.signatures_mut().retain(|s| {
            root_key_ids.contains(s.key_id())
        });
        Self::from_root(signed_root)
    }

    /// Create a new `TUF` struct from a piece of metadata that is assumed to be trusted.
    ///
    /// *WARNING*: This is trust-on-first-use (TOFU) and offers weaker security guarantees than the
    /// related method `from_root_pinned`.
    pub fn from_root<V>(signed_root: SignedMetadata<D, RootMetadata, V>) -> Result<Self>
    where
        V: VerificationStatus,
    {
        let root = D::deserialize::<RootMetadata>(signed_root.unverified_signed())?;
        let _ = signed_root.verify(
            root.root().threshold(),
            root.root().key_ids(),
            root.keys(),
        )?;
        Ok(Tuf {
            root: root,
            snapshot: None,
            targets: None,
            timestamp: None,
            _interchange: PhantomData,
        })
    }

    /// An immutable reference to the root metadata.
    pub fn root(&self) -> &RootMetadata {
        &self.root
    }

    /// An immutable reference to the optional snapshot metadata.
    pub fn snapshot(&self) -> Option<&SnapshotMetadata> {
        self.snapshot.as_ref()
    }

    /// An immutable reference to the optional targets metadata.
    pub fn targets(&self) -> Option<&TargetsMetadata> {
        self.targets.as_ref()
    }

    /// An immutable reference to the optional timestamp metadata.
    pub fn timestamp(&self) -> Option<&TimestampMetadata> {
        self.timestamp.as_ref()
    }

    /// Return the list of all available targets.
    pub fn available_targets(&self) -> Result<HashSet<&TargetPath>> {
        let _ = self.safe_root_ref()?; // ensure root still valid
        // TODO add delegations
        Ok(
            self.safe_targets_ref()?
                .targets()
                .keys()
                .collect::<HashSet<&TargetPath>>(),
        )
    }

    /// Verify and update the root metadata.
    pub fn update_root<V>(
        &mut self,
        signed_root: SignedMetadata<D, RootMetadata, V>,
    ) -> Result<bool>
    where
        V: VerificationStatus,
    {
        let signed_root = signed_root.verify(
            self.root.root().threshold(),
            self.root.root().key_ids(),
            self.root.keys(),
        )?;

        let root = D::deserialize::<RootMetadata>(signed_root.unverified_signed())?;

        match root.version() {
            x if x == self.root.version() => {
                info!(
                    "Attempted to update root to new metadata with the same version. \
                      Refusing to update."
                );
                return Ok(false);
            }
            x if x < self.root.version() => {
                return Err(Error::VerificationFailure(format!(
                    "Attempted to roll back root metadata at version {} to {}.",
                    self.root.version(),
                    x
                )))
            }
            _ => (),
        }

        let _ = signed_root.verify(
            root.root().threshold(),
            root.root().key_ids(),
            root.keys(),
        )?;

        self.purge_metadata();

        self.root = root;
        Ok(true)
    }

    /// Verify and update the timestamp metadata.
    pub fn update_timestamp<V>(
        &mut self,
        signed_timestamp: SignedMetadata<D, TimestampMetadata, V>,
    ) -> Result<bool>
    where
        V: VerificationStatus,
    {
        let signed_timestamp = signed_timestamp.verify(
            self.root.timestamp().threshold(),
            self.root.timestamp().key_ids(),
            self.root.keys(),
        )?;

        let current_version = self.timestamp.as_ref().map(|t| t.version()).unwrap_or(0);
        let timestamp: TimestampMetadata = D::deserialize(&signed_timestamp.signed())?;

        if timestamp.expires() <= &Utc::now() {
            return Err(Error::ExpiredMetadata(Role::Timestamp));
        }

        if timestamp.version() < current_version {
            Err(Error::VerificationFailure(format!(
                "Attempted to roll back timestamp metadata at version {} to {}.",
                current_version,
                timestamp.version()
            )))
        } else if timestamp.version() == current_version {
            Ok(false)
        } else {
            self.timestamp = Some(timestamp);
            Ok(true)
        }
    }

    /// Verify and update the snapshot metadata.
    pub fn update_snapshot<V>(
        &mut self,
        signed_snapshot: SignedMetadata<D, SnapshotMetadata, V>,
    ) -> Result<bool>
    where
        V: VerificationStatus,
    {
        let snapshot = {
            let root = self.safe_root_ref()?;
            let timestamp = self.safe_timestamp_ref()?;
            let snapshot_description = timestamp
                .meta()
                .get(&MetadataPath::from_role(&Role::Snapshot))
                .ok_or_else(|| {
                    Error::VerificationFailure(
                        "Timestamp metadata had no description of the snapshot metadata".into(),
                    )
                })?;

            let signed_snapshot = signed_snapshot.verify(
                root.snapshot().threshold(),
                root.snapshot().key_ids(),
                root.keys(),
            )?;

            let current_version = self.snapshot.as_ref().map(|t| t.version()).unwrap_or(0);
            let snapshot: SnapshotMetadata = D::deserialize(&signed_snapshot.signed())?;

            if snapshot.version() != snapshot_description.version() {
                return Err(Error::VerificationFailure(format!(
                    "The timestamp metadata reported that the snapshot metadata should be at \
                    version {} but version {} was found instead.",
                    snapshot_description.version(),
                    snapshot.version()
                )));
            }

            if snapshot.expires() <= &Utc::now() {
                return Err(Error::ExpiredMetadata(Role::Snapshot));
            }

            if snapshot.version() < current_version {
                return Err(Error::VerificationFailure(format!(
                    "Attempted to roll back snapshot metadata at version {} to {}.",
                    current_version,
                    snapshot.version()
                )));
            } else if snapshot.version() == current_version {
                return Ok(false);
            } else {
                snapshot
            }
        };

        self.snapshot = Some(snapshot);
        Ok(true)
    }

    /// Verify and update the targets metadata.
    pub fn update_targets<V>(
        &mut self,
        signed_targets: SignedMetadata<D, TargetsMetadata, V>,
    ) -> Result<bool>
    where
        V: VerificationStatus,
    {
        let targets = {
            let root = self.safe_root_ref()?;
            let snapshot = self.safe_snapshot_ref()?;
            let targets_description = snapshot
                .meta()
                .get(&MetadataPath::from_role(&Role::Targets))
                .ok_or_else(|| {
                    Error::VerificationFailure(
                        "Snapshot metadata had no description of the targets metadata".into(),
                    )
                })?;

            let signed_targets = signed_targets.verify(
                root.targets().threshold(),
                root.targets().key_ids(),
                root.keys(),
            )?;

            let current_version = self.targets.as_ref().map(|t| t.version()).unwrap_or(0);
            let targets: TargetsMetadata = D::deserialize(&signed_targets.signed())?;

            if targets.version() != targets_description.version() {
                return Err(Error::VerificationFailure(format!(
                    "The timestamp metadata reported that the targets metadata should be at \
                    version {} but version {} was found instead.",
                    targets_description.version(),
                    targets.version()
                )));
            }

            if targets.expires() <= &Utc::now() {
                return Err(Error::ExpiredMetadata(Role::Snapshot));
            }

            if targets.version() < current_version {
                return Err(Error::VerificationFailure(format!(
                    "Attempted to roll back targets metadata at version {} to {}.",
                    current_version,
                    targets.version()
                )));
            } else if targets.version() == current_version {
                return Ok(false);
            } else {
                targets
            }
        };

        self.targets = Some(targets);
        Ok(true)
    }

    fn purge_metadata(&mut self) {
        self.snapshot = None;
        self.targets = None;
        self.timestamp = None;
        // TODO include delegations
    }

    fn safe_root_ref(&self) -> Result<&RootMetadata> {
        if self.root.expires() <= &Utc::now() {
            return Err(Error::ExpiredMetadata(Role::Root));
        }
        Ok(&self.root)
    }

    fn safe_snapshot_ref(&self) -> Result<&SnapshotMetadata> {
        match &self.snapshot {
            &Some(ref snapshot) => {
                if snapshot.expires() <= &Utc::now() {
                    return Err(Error::ExpiredMetadata(Role::Snapshot));
                }
                Ok(snapshot)
            }
            &None => Err(Error::MissingMetadata(Role::Snapshot)),
        }
    }

    fn safe_targets_ref(&self) -> Result<&TargetsMetadata> {
        match &self.targets {
            &Some(ref targets) => {
                if targets.expires() <= &Utc::now() {
                    return Err(Error::ExpiredMetadata(Role::Targets));
                }
                Ok(targets)
            }
            &None => Err(Error::MissingMetadata(Role::Targets)),
        }
    }
    fn safe_timestamp_ref(&self) -> Result<&TimestampMetadata> {
        match &self.timestamp {
            &Some(ref timestamp) => {
                if timestamp.expires() <= &Utc::now() {
                    return Err(Error::ExpiredMetadata(Role::Timestamp));
                }
                Ok(timestamp)
            }
            &None => Err(Error::MissingMetadata(Role::Timestamp)),
        }
    }
}
<|endoftext|>