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

use dom::bindings::codegen::Bindings::RequestBinding::RequestInfo;
use dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
use dom::bindings::codegen::Bindings::ResponseBinding::ResponseBinding::ResponseMethods;
use dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
use dom::bindings::error::Error;
use dom::bindings::inheritance::Castable;
use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::DomObject;
use dom::bindings::root::DomRoot;
use dom::bindings::trace::RootedTraceableBox;
use dom::globalscope::GlobalScope;
use dom::headers::Guard;
use dom::promise::Promise;
use dom::request::Request;
use dom::response::Response;
use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsapi::JSAutoCompartment;
use net_traits::{FetchChannels, FetchResponseListener, NetworkError};
use net_traits::{FilteredMetadata, FetchMetadata, Metadata};
use net_traits::CoreResourceMsg::Fetch as NetTraitsFetch;
use net_traits::request::{Request as NetTraitsRequest, ServiceWorkersMode};
use net_traits::request::RequestInit as NetTraitsRequestInit;
use network_listener::{NetworkListener, PreInvoke};
use servo_url::ServoUrl;
use std::mem;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

struct FetchContext {
    fetch_promise: Option<TrustedPromise>,
    response_object: Trusted<Response>,
    body: Vec<u8>,
}

fn from_referrer_to_referrer_url(request: &NetTraitsRequest) -> Option<ServoUrl> {
    request.referrer.to_url().map(|url| url.clone())
}

fn request_init_from_request(request: NetTraitsRequest) -> NetTraitsRequestInit {
    NetTraitsRequestInit {
        method: request.method.clone(),
        url: request.url(),
        headers: request.headers.clone(),
        unsafe_request: request.unsafe_request,
        body: request.body.clone(),
        destination: request.destination,
        synchronous: request.synchronous,
        mode: request.mode.clone(),
        use_cors_preflight: request.use_cors_preflight,
        credentials_mode: request.credentials_mode,
        use_url_credentials: request.use_url_credentials,
        origin: GlobalScope::current().expect("No current global object").origin().immutable().clone(),
        referrer_url: from_referrer_to_referrer_url(&request),
        referrer_policy: request.referrer_policy,
        pipeline_id: request.pipeline_id,
        redirect_mode: request.redirect_mode,
        cache_mode: request.cache_mode,
        ..NetTraitsRequestInit::default()
    }
}

// https://fetch.spec.whatwg.org/#fetch-method
#[allow(unrooted_must_root)]
pub fn Fetch(global: &GlobalScope, input: RequestInfo, init: RootedTraceableBox<RequestInit>) -> Rc<Promise> {
    let core_resource_thread = global.core_resource_thread();

    // Step 1
    let promise = Promise::new(global);
    let response = Response::new(global);

    // Step 2
    let request = match Request::Constructor(global, input, init) {
        Err(e) => {
            promise.reject_error(e);
            return promise;
        },
        Ok(r) => r.get_request(),
    };
    let mut request_init = request_init_from_request(request);

    // Step 3
    if global.downcast::<ServiceWorkerGlobalScope>().is_some() {
        request_init.service_workers_mode = ServiceWorkersMode::Foreign;
    }

    // Step 4
    response.Headers().set_guard(Guard::Immutable);

    // Step 5
    let (action_sender, action_receiver) = ipc::channel().unwrap();
    let fetch_context = Arc::new(Mutex::new(FetchContext {
        fetch_promise: Some(TrustedPromise::new(promise.clone())),
        response_object: Trusted::new(&*response),
        body: vec![],
    }));
    let listener = NetworkListener {
        context: fetch_context,
        task_source: global.networking_task_source(),
        canceller: Some(global.task_canceller())
    };

    ROUTER.add_route(action_receiver.to_opaque(), Box::new(move |message| {
        listener.notify_fetch(message.to().unwrap());
    }));
    core_resource_thread.send(
        NetTraitsFetch(request_init, FetchChannels::ResponseMsg(action_sender, None))).unwrap();

    promise
}

impl PreInvoke for FetchContext {}

impl FetchResponseListener for FetchContext {
    fn process_request_body(&mut self) {
        // TODO
    }

    fn process_request_eof(&mut self) {
        // TODO
    }

    #[allow(unrooted_must_root)]
    fn process_response(&mut self, fetch_metadata: Result<FetchMetadata, NetworkError>) {
        let promise = self.fetch_promise.take().expect("fetch promise is missing").root();

        // JSAutoCompartment needs to be manually made.
        // Otherwise, Servo will crash.
        let promise_cx = promise.global().get_cx();
        let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
        match fetch_metadata {
            // Step 4.1
            Err(_) => {
                promise.reject_error(Error::Type("Network error occurred".to_string()));
                self.fetch_promise = Some(TrustedPromise::new(promise));
                self.response_object.root().set_type(DOMResponseType::Error);
                return;
            },
            // Step 4.2
            Ok(metadata) => {
                match metadata {
                    FetchMetadata::Unfiltered(m) => {
                        fill_headers_with_metadata(self.response_object.root(), m);
                        self.response_object.root().set_type(DOMResponseType::Default);
                    },
                    FetchMetadata::Filtered { filtered, .. } => match filtered {
                        FilteredMetadata::Basic(m) => {
                            fill_headers_with_metadata(self.response_object.root(), m);
                            self.response_object.root().set_type(DOMResponseType::Basic);
                        },
                        FilteredMetadata::Cors(m) => {
                            fill_headers_with_metadata(self.response_object.root(), m);
                            self.response_object.root().set_type(DOMResponseType::Cors);
                        },
                        FilteredMetadata::Opaque =>
                            self.response_object.root().set_type(DOMResponseType::Opaque),
                        FilteredMetadata::OpaqueRedirect =>
                            self.response_object.root().set_type(DOMResponseType::Opaqueredirect)
                    }
                }
            }
        }
        // Step 4.3
        promise.resolve_native(&self.response_object.root());
        self.fetch_promise = Some(TrustedPromise::new(promise));
    }

    fn process_response_chunk(&mut self, mut chunk: Vec<u8>) {
        self.body.append(&mut chunk);
    }

    fn process_response_eof(&mut self, _response: Result<(), NetworkError>) {
        let response = self.response_object.root();
        let global = response.global();
        let cx = global.get_cx();
        let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
        response.finish(mem::replace(&mut self.body, vec![]));
        // TODO
        // ... trailerObject is not supported in Servo yet.
    }
}

fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata) {
    r.set_headers(m.headers);
    r.set_raw_status(m.status);
    r.set_final_url(m.final_url);
}
<commit_msg>Add FetchCanceller RAII handle<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::Bindings::RequestBinding::RequestInfo;
use dom::bindings::codegen::Bindings::RequestBinding::RequestInit;
use dom::bindings::codegen::Bindings::ResponseBinding::ResponseBinding::ResponseMethods;
use dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
use dom::bindings::error::Error;
use dom::bindings::inheritance::Castable;
use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::DomObject;
use dom::bindings::root::DomRoot;
use dom::bindings::trace::RootedTraceableBox;
use dom::globalscope::GlobalScope;
use dom::headers::Guard;
use dom::promise::Promise;
use dom::request::Request;
use dom::response::Response;
use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsapi::JSAutoCompartment;
use net_traits::{FetchChannels, FetchResponseListener, NetworkError};
use net_traits::{FilteredMetadata, FetchMetadata, Metadata};
use net_traits::CoreResourceMsg::Fetch as NetTraitsFetch;
use net_traits::request::{Request as NetTraitsRequest, ServiceWorkersMode};
use net_traits::request::RequestInit as NetTraitsRequestInit;
use network_listener::{NetworkListener, PreInvoke};
use servo_url::ServoUrl;
use std::mem;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

struct FetchContext {
    fetch_promise: Option<TrustedPromise>,
    response_object: Trusted<Response>,
    body: Vec<u8>,
}

/// RAII fetch canceller object. By default initialized to not having a canceller
/// in it, however you can ask it for a cancellation receiver to send to Fetch
/// in which case it will store the sender. You can manually cancel it
/// or let it cancel on Drop in that case.
#[derive(Default, JSTraceable, MallocSizeOf)]
pub struct FetchCanceller {
    #[ignore_malloc_size_of = "channels are hard"]
    cancel_chan: Option<ipc::IpcSender<()>>
}

impl FetchCanceller {
    /// Create an empty FetchCanceller
    pub fn new() -> Self {
        Default::default()
    }

    /// Obtain an IpcReceiver to send over to Fetch, and initialize
    /// the internal sender
    pub fn initialize(&mut self) -> ipc::IpcReceiver<()> {
        // cancel previous fetch
        self.cancel();
        let (rx, tx) = ipc::channel().unwrap();
        self.cancel_chan = Some(rx);
        tx
    }

    /// Cancel a fetch if it is ongoing
    pub fn cancel(&mut self) {
        if let Some(chan) = self.cancel_chan.take() {
            // stop trying to make fetch happen
            // it's not going to happen

            // The receiver will be destroyed if the request has already completed;
            // so we throw away the error. Cancellation is a courtesy call,
            // we don't actually care if the other side heard.
            let _ = chan.send(());
        }
    }

    /// Use this if you don't want it to send a cancellation request
    /// on drop (e.g. if the fetch completes)
    pub fn ignore(&mut self) {
        let _ = self.cancel_chan.take();
    }
}

impl Drop for FetchCanceller {
    fn drop(&mut self) {
        self.cancel()
    }
}

fn from_referrer_to_referrer_url(request: &NetTraitsRequest) -> Option<ServoUrl> {
    request.referrer.to_url().map(|url| url.clone())
}

fn request_init_from_request(request: NetTraitsRequest) -> NetTraitsRequestInit {
    NetTraitsRequestInit {
        method: request.method.clone(),
        url: request.url(),
        headers: request.headers.clone(),
        unsafe_request: request.unsafe_request,
        body: request.body.clone(),
        destination: request.destination,
        synchronous: request.synchronous,
        mode: request.mode.clone(),
        use_cors_preflight: request.use_cors_preflight,
        credentials_mode: request.credentials_mode,
        use_url_credentials: request.use_url_credentials,
        origin: GlobalScope::current().expect("No current global object").origin().immutable().clone(),
        referrer_url: from_referrer_to_referrer_url(&request),
        referrer_policy: request.referrer_policy,
        pipeline_id: request.pipeline_id,
        redirect_mode: request.redirect_mode,
        cache_mode: request.cache_mode,
        ..NetTraitsRequestInit::default()
    }
}

// https://fetch.spec.whatwg.org/#fetch-method
#[allow(unrooted_must_root)]
pub fn Fetch(global: &GlobalScope, input: RequestInfo, init: RootedTraceableBox<RequestInit>) -> Rc<Promise> {
    let core_resource_thread = global.core_resource_thread();

    // Step 1
    let promise = Promise::new(global);
    let response = Response::new(global);

    // Step 2
    let request = match Request::Constructor(global, input, init) {
        Err(e) => {
            promise.reject_error(e);
            return promise;
        },
        Ok(r) => r.get_request(),
    };
    let mut request_init = request_init_from_request(request);

    // Step 3
    if global.downcast::<ServiceWorkerGlobalScope>().is_some() {
        request_init.service_workers_mode = ServiceWorkersMode::Foreign;
    }

    // Step 4
    response.Headers().set_guard(Guard::Immutable);

    // Step 5
    let (action_sender, action_receiver) = ipc::channel().unwrap();
    let fetch_context = Arc::new(Mutex::new(FetchContext {
        fetch_promise: Some(TrustedPromise::new(promise.clone())),
        response_object: Trusted::new(&*response),
        body: vec![],
    }));
    let listener = NetworkListener {
        context: fetch_context,
        task_source: global.networking_task_source(),
        canceller: Some(global.task_canceller())
    };

    ROUTER.add_route(action_receiver.to_opaque(), Box::new(move |message| {
        listener.notify_fetch(message.to().unwrap());
    }));
    core_resource_thread.send(
        NetTraitsFetch(request_init, FetchChannels::ResponseMsg(action_sender, None))).unwrap();

    promise
}

impl PreInvoke for FetchContext {}

impl FetchResponseListener for FetchContext {
    fn process_request_body(&mut self) {
        // TODO
    }

    fn process_request_eof(&mut self) {
        // TODO
    }

    #[allow(unrooted_must_root)]
    fn process_response(&mut self, fetch_metadata: Result<FetchMetadata, NetworkError>) {
        let promise = self.fetch_promise.take().expect("fetch promise is missing").root();

        // JSAutoCompartment needs to be manually made.
        // Otherwise, Servo will crash.
        let promise_cx = promise.global().get_cx();
        let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
        match fetch_metadata {
            // Step 4.1
            Err(_) => {
                promise.reject_error(Error::Type("Network error occurred".to_string()));
                self.fetch_promise = Some(TrustedPromise::new(promise));
                self.response_object.root().set_type(DOMResponseType::Error);
                return;
            },
            // Step 4.2
            Ok(metadata) => {
                match metadata {
                    FetchMetadata::Unfiltered(m) => {
                        fill_headers_with_metadata(self.response_object.root(), m);
                        self.response_object.root().set_type(DOMResponseType::Default);
                    },
                    FetchMetadata::Filtered { filtered, .. } => match filtered {
                        FilteredMetadata::Basic(m) => {
                            fill_headers_with_metadata(self.response_object.root(), m);
                            self.response_object.root().set_type(DOMResponseType::Basic);
                        },
                        FilteredMetadata::Cors(m) => {
                            fill_headers_with_metadata(self.response_object.root(), m);
                            self.response_object.root().set_type(DOMResponseType::Cors);
                        },
                        FilteredMetadata::Opaque =>
                            self.response_object.root().set_type(DOMResponseType::Opaque),
                        FilteredMetadata::OpaqueRedirect =>
                            self.response_object.root().set_type(DOMResponseType::Opaqueredirect)
                    }
                }
            }
        }
        // Step 4.3
        promise.resolve_native(&self.response_object.root());
        self.fetch_promise = Some(TrustedPromise::new(promise));
    }

    fn process_response_chunk(&mut self, mut chunk: Vec<u8>) {
        self.body.append(&mut chunk);
    }

    fn process_response_eof(&mut self, _response: Result<(), NetworkError>) {
        let response = self.response_object.root();
        let global = response.global();
        let cx = global.get_cx();
        let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
        response.finish(mem::replace(&mut self.body, vec![]));
        // TODO
        // ... trailerObject is not supported in Servo yet.
    }
}

fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata) {
    r.set_headers(m.headers);
    r.set_raw_status(m.status);
    r.set_final_url(m.final_url);
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
const END_TAG_LEN: u32 = 8;

use core::mem::transmute;

#[macro_use]
macro_rules! ptr {
    ($it:expr) => { $it as *const _ }
}

pub mod elf;
pub mod elf64;

#[repr(C)]
pub struct Info { pub length: u32
                , _pad: u32
                , tag_start: Tag
                }

impl Info {

    pub unsafe fn from(addr: usize) -> &'static Self {
        let info = &*(addr as *const Info);
        assert!(info.has_end());
        info
    }

    /// Finds the tag with the given tag type.
    ///
    /// This is actually safe since the tag types are constrained by The
    /// `TagType` enum
    pub fn get_tag(&self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags()
            .find(|t| t.ty == tag_type)
    }

    #[inline]
    pub fn mem_map(&self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::MemoryMap)
            .map(|tag| unsafe { &*(ptr!(tag) as *const MemMapTag) })
    }

    #[inline]
    pub fn elf64_sections(&self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::ElfSections)
            .map(|tag| unsafe { &*(ptr!(tag) as *const MemMapTag) })
    }

    #[inline]
    fn tags(&self) -> Tags { Tags(ptr!(self.tag_start)) }

    fn has_end(&self) -> bool {
        let end_tag_addr
            = (self as *const _) as usize +
              (self.length - END_TAG_LEN) as usize;
        let end_tag = unsafe {&*(end_tag_addr as *const Tag)};
        end_tag.ty == TagType::End && end_tag.length == 8
    }
}

/// A Multiboot tag.
///
/// From the specification:
///
/// Boot information consists of a fixed part and a series of tags.
/// Its start is 8-bytes aligned. Fixed part is as following:
///
///<rawtext>
///             +-------------------+
///     u32     | total_size        |
///     u32     | reserved          |
///             +-------------------+
///</rawtext>
/// `total_size` contains the total size of boot information including this
/// field and terminating tag in bytes.
/// `reserved` is always set to zero and must be ignored by OS image.
///
///  Every tag begins with following fields:
///<rawtext>
///
///             +-------------------+
///     u32     | type              |
///     u32     | size              |
///             +-------------------+
///</rawtext>
/// `type` contains an identifier of contents of the rest of the tag. `size`
/// contains the size of tag including header fields but not including padding.
/// Tags follow one another padded when necessary in order for each tag to
/// start at 8-bytes aligned address. Tags are terminated by a tag of type `0`
/// and size `8`.
#[repr(C)]
#[derive(Debug)]
struct Tag { ty: TagType
           , length: u32
           }

/// Types of Multiboot tags
///
/// Refer to Chapter 3 of the Multiboot 2 spec
#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
enum TagType { /// Tag that indicates the end of multiboot tags
               End              = 0
             , /// Command line passed to the bootloader
               CommandLine      = 1
             , BootloaderName   = 2
             , Modules          = 3
             , BasicMemInfo     = 4
             , BIOSBootDev      = 5
             , MemoryMap        = 6
             , VBEInfo          = 7
             , FramebufferInfo  = 8
             , ELFSections      = 9
             , APMTable         = 10
             }

struct Tags(*const Tag);

impl Tags {
    #[inline] fn advance(&mut self, size: u32) {
        let mut next_addr = self.0 as usize + size as usize;
        next_addr = ((next_addr-1) & !0x7) + 0x8; //align at 8 byte
        self.0 = ptr!(next_addr);
    }
}

impl Iterator for Tags {
    type Item = &'static Tag;

    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { &*self.0 } {
            &Tag{ ty: TagType::End, length: END_TAG_LEN } => None
          , tag @ &Tag { ty: _, length: l } => { self.advance(l); Some(tag) }
        }
    }
}

#[repr(C)]
pub struct MemMapTag { tag: Tag
                     , entry_size: u32
                     , entry_version: u32
                     , first_entry: MemArea
                     }
impl MemMapTag {
    pub fn entries(&self) -> Entries {
        Entries { curr: (&self.first_entry) as *const MemArea
                , last: ((self as *const MemMapTag as u32)
                            + self.size
                            - self.entry_size) as *const MemArea
                , size: self.entry_size
                }
    }
}

#[repr(u32)]
pub enum MemAreaType { Available = 1
                     , ACPI      = 3
                     , Preserve  = 4
                     }

#[repr(C)]
pub struct MemArea { pub base: u64
                   , pub length: u64
                   , ty: MemAreaType
                   , _pad: u32
                   }

pub struct Entries { curr: *const MemArea
                   , last: *const MemArea
                   , size: u32
                   }

impl Iterator for Entries {
    type Item = &'static MemArea;
    fn next(&mut self) -> Option<&'static MemArea> {
        if self.curr > self.last {
            None
        } else {
            let current = self.curr;
            self.curr = (self.curr as u32 + self.size) as *const MemArea;
            if current.type == MemAreaType::Available {
                Some(current)
            } else {
                self.next();
            }
        }
    }
}
<commit_msg>Fix typo in ptr!() macro<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
const END_TAG_LEN: u32 = 8;

use core::mem::transmute;

#[macro_use]
macro_rules! ptr {
    ($it:expr) => { &$it as *const _ }
}

pub mod elf;
pub mod elf64;

#[repr(C)]
pub struct Info { pub length: u32
                , _pad: u32
                , tag_start: Tag
                }

impl Info {

    pub unsafe fn from(addr: usize) -> &'static Self {
        let info = &*(addr as *const Info);
        assert!(info.has_end());
        info
    }

    /// Finds the tag with the given tag type.
    ///
    /// This is actually safe since the tag types are constrained by The
    /// `TagType` enum
    pub fn get_tag(&self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags()
            .find(|t| t.ty == tag_type)
    }

    #[inline]
    pub fn mem_map(&self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::MemoryMap)
            .map(|tag| unsafe { &*(ptr!(tag) as *const MemMapTag) })
    }

    #[inline]
    pub fn elf64_sections(&self) -> Option<&'static MemMapTag> {
        self.get_tag(TagType::ElfSections)
            .map(|tag| unsafe { &*(ptr!(tag) as *const MemMapTag) })
    }

    #[inline]
    fn tags(&self) -> Tags { Tags(ptr!(self.tag_start)) }

    fn has_end(&self) -> bool {
        let end_tag_addr
            = (self as *const _) as usize +
              (self.length - END_TAG_LEN) as usize;
        let end_tag = unsafe {&*(end_tag_addr as *const Tag)};
        end_tag.ty == TagType::End && end_tag.length == 8
    }
}

/// A Multiboot tag.
///
/// From the specification:
///
/// Boot information consists of a fixed part and a series of tags.
/// Its start is 8-bytes aligned. Fixed part is as following:
///
///<rawtext>
///             +-------------------+
///     u32     | total_size        |
///     u32     | reserved          |
///             +-------------------+
///</rawtext>
/// `total_size` contains the total size of boot information including this
/// field and terminating tag in bytes.
/// `reserved` is always set to zero and must be ignored by OS image.
///
///  Every tag begins with following fields:
///<rawtext>
///
///             +-------------------+
///     u32     | type              |
///     u32     | size              |
///             +-------------------+
///</rawtext>
/// `type` contains an identifier of contents of the rest of the tag. `size`
/// contains the size of tag including header fields but not including padding.
/// Tags follow one another padded when necessary in order for each tag to
/// start at 8-bytes aligned address. Tags are terminated by a tag of type `0`
/// and size `8`.
#[repr(C)]
#[derive(Debug)]
struct Tag { ty: TagType
           , length: u32
           }

/// Types of Multiboot tags
///
/// Refer to Chapter 3 of the Multiboot 2 spec
#[repr(u32)]
#[derive(Debug, Eq, PartialEq)]
enum TagType { /// Tag that indicates the end of multiboot tags
               End              = 0
             , /// Command line passed to the bootloader
               CommandLine      = 1
             , BootloaderName   = 2
             , Modules          = 3
             , BasicMemInfo     = 4
             , BIOSBootDev      = 5
             , MemoryMap        = 6
             , VBEInfo          = 7
             , FramebufferInfo  = 8
             , ELFSections      = 9
             , APMTable         = 10
             }

struct Tags(*const Tag);

impl Tags {
    #[inline] fn advance(&mut self, size: u32) {
        let mut next_addr = self.0 as usize + size as usize;
        next_addr = ((next_addr-1) & !0x7) + 0x8; //align at 8 byte
        self.0 = ptr!(next_addr);
    }
}

impl Iterator for Tags {
    type Item = &'static Tag;

    fn next(&mut self) -> Option<Self::Item> {
        match unsafe { &*self.0 } {
            &Tag{ ty: TagType::End, length: END_TAG_LEN } => None
          , tag @ &Tag { ty: _, length: l } => { self.advance(l); Some(tag) }
        }
    }
}

#[repr(C)]
pub struct MemMapTag { tag: Tag
                     , entry_size: u32
                     , entry_version: u32
                     , first_entry: MemArea
                     }
impl MemMapTag {
    pub fn entries(&self) -> Entries {
        Entries { curr: (&self.first_entry) as *const MemArea
                , last: ((self as *const MemMapTag as u32)
                            + self.size
                            - self.entry_size) as *const MemArea
                , size: self.entry_size
                }
    }
}

#[repr(u32)]
pub enum MemAreaType { Available = 1
                     , ACPI      = 3
                     , Preserve  = 4
                     }

#[repr(C)]
pub struct MemArea { pub base: u64
                   , pub length: u64
                   , ty: MemAreaType
                   , _pad: u32
                   }

pub struct Entries { curr: *const MemArea
                   , last: *const MemArea
                   , size: u32
                   }

impl Iterator for Entries {
    type Item = &'static MemArea;
    fn next(&mut self) -> Option<&'static MemArea> {
        if self.curr > self.last {
            None
        } else {
            let current = self.curr;
            self.curr = (self.curr as u32 + self.size) as *const MemArea;
            if current.type == MemAreaType::Available {
                Some(current)
            } else {
                self.next();
            }
        }
    }
}
<|endoftext|>


<commit_before>use neat::*;

#[derive(Debug, Clone)]
pub struct Organism {
    pub genome: Genome,
    pub fitness: f64,
    pub neurons: Vec<Option<Neuron>>
}

const ACTIVATION_CYCLES: usize = 10;

impl Organism {
    pub fn new(genome: Genome) -> Organism {
        Organism { genome: genome, fitness: 0f64, neurons: vec![] }
    }

    pub fn mutate(&self) -> Organism {
        let mut new_genome = self.genome.clone();
        new_genome.mutate();
        Organism::new(new_genome)
    }

    pub fn mate(&self, other: &Organism) -> Organism {
        Organism::new(self.genome.mate(&other.genome, self.fitness < other.fitness))
    }

    pub fn activate_old(&mut self, sensors: &Vec<f64>, outputs: &mut Vec<f64>){
       if self.neurons.len() == 0 {
           self.generate_phenome();
       };

       for _n in 0..ACTIVATION_CYCLES {
           for neuron_id in 0..sensors.len() {
               if neuron_id < self.neurons.len() {
                   self.neurons[neuron_id].as_mut().unwrap().stimulate(sensors[neuron_id]);
               }
           }

           self.activate_neurons();
           self.propagate_signals();
       }

       //Take outputs from next neurons after sensors
       let sensors_len = sensors.len();
       for neuron_id in sensors_len..(sensors_len + outputs.len()){
           if neuron_id < self.neurons.len() {
               outputs[neuron_id - sensors_len] = self.neurons[neuron_id].as_ref().map_or(0f64, |neuron| neuron.potential());
           } else {
               outputs[neuron_id - sensors_len] = 0f64;
           }
       }

       for neuron in &mut self.neurons {
           if neuron.is_some() {
               neuron.as_mut().unwrap().relax();
           }
       }
    }

    pub fn activate(&mut self, sensors: &Vec<f64>, outputs: &mut Vec<f64>){

        let neurons_len = self.genome.len();
        let gamma = vec![0.0; neurons_len];
        let tau = vec![10.0; neurons_len];
        let theta = vec![0.0; neurons_len];
        let wik = vec![1.0; sensors.len() * neurons_len];
        let i = sensors.clone();
        let activations = Ctrnn::new().activate(30, &gamma, 1.0, &tau, &self.get_weights_matrix(), &theta, &(neurons_len, sensors.len(), wik), &i);
        if sensors.len() < neurons_len {
            let outputs_activations = activations.split_at(sensors.len()).1.to_vec();
            for n in 0..outputs_activations.len() {
                outputs[n] = outputs_activations[n];
            }
        }
    }

    fn activate_neurons(&mut self){
        for neuron_id in 0..self.neurons.len() {
            self.neurons[neuron_id].as_mut().unwrap().shot();
        }
    }

    fn propagate_signals(&mut self){
        for neuron_option in self.neurons.clone() {
            let neuron = neuron_option.unwrap();
            for connection in &neuron.connections {
               self.neurons[connection.0].as_mut().unwrap().stimulate(neuron.potential() * connection.1);
            }
        }
    }

    fn generate_phenome(&mut self){
        let neurons_to_generate = self.genome.len();

        self.neurons = vec![Some(Neuron::new()); neurons_to_generate as usize];

        for gene in self.genome.get_genes() {
            let mut neuron = self.neurons[gene.in_neuron_id].as_mut().unwrap();
            neuron.connections.push(Connection(gene.out_neuron_id, gene.weight));
        }
    }

    fn get_weights_matrix(&self) -> (usize, usize, Vec<f64>){
        let neurons_len = self.genome.len();
        let mut matrix = vec![0.0; neurons_len * neurons_len];
        for gene in self.genome.get_genes() {
            if gene.enabled {
                matrix[(gene.in_neuron_id * neurons_len) + gene.out_neuron_id] = gene.weight
            }
        }
        (neurons_len, neurons_len, matrix)
    }
}

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

    #[test]
    fn should_be_able_to_generate_his_phenome(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 0, 0.1f64);
        organism.genome.inject_gene(0, 1, 0.2f64);
        organism.genome.inject_gene(1, 0, 0.3f64);
        organism.genome.inject_gene(1, 1, 0.4f64);
        organism.generate_phenome();
        assert!(organism.neurons.len() == 2);
        let neuron0 = &organism.neurons[0].as_ref().unwrap();
        let connection00 = &neuron0.connections[0];
        assert!(connection00.0 == 0usize);
        assert!(connection00.1 == 0.1f64);
        let connection01 = &neuron0.connections[1];
        assert!(connection01.0 == 1usize);
        assert!(connection01.1 == 0.2f64);
        let neuron1 = &organism.neurons[1].as_ref().unwrap();
        let connection10 = &neuron1.connections[0];
        assert!(connection10.0 == 0usize);
        assert!(connection10.1 == 0.3f64);
        let connection11 = &neuron1.connections[1];
        assert!(connection11.0 == 1usize);
        assert!(connection11.1 == 0.4f64);
    }

    #[test]
    fn should_propagate_signal_without_hidden_layers(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 5f64);
        let sensors = vec![1f64];
        let mut output = vec![0f64];
        organism.activate(&sensors, &mut output);
        assert!(output[0] > 0f64, format!("{:?} is not bigger than 0", output[0]));

        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, -5f64);
        let sensors = vec![1f64];
        let mut output = vec![0f64];
        organism.activate(&sensors, &mut output);
        assert!(output[0] < 0.1f64, format!("{:?} is not smaller than 0.1", output[0]));
    }

    #[test]
    fn should_propagate_signal_over_hidden_layers(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 0f64); //disable connection
        organism.genome.inject_gene(0, 2, 1f64);
        organism.genome.inject_gene(2, 1, 1f64);
        let sensors = vec![0f64];
        let mut output = vec![0f64];
        organism.activate(&sensors, &mut output);
        assert!(output[0] > 0.9f64, format!("{:?} is not bigger than 0.9", output[0]));
    }

    #[test]
    fn should_work_with_cyclic_networks(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 1f64);
        organism.genome.inject_gene(1, 2, 1f64);
        organism.genome.inject_gene(2, 1, 1f64);
        let mut output = vec![0f64];
        organism.activate(&vec![1f64], &mut output);
        assert!(output[0] > 0.9, format!("{:?} is not bigger than 0.9", output[0]));

        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, -1f64);
        organism.genome.inject_gene(1, 2, -1f64);
        organism.genome.inject_gene(2, 1, -1f64);
        let mut output = vec![0f64];
        organism.activate(&vec![1f64], &mut output);
        assert!(output[0] < 0.1, format!("{:?} is not smaller than 0.1", output[0]));
    }

    #[test]
    fn activate_organims_sensor_without_enough_neurons_should_ignore_it(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 1f64);
        let sensors = vec![0f64,0f64,0f64];
        let mut output = vec![0f64];
        organism.activate(&sensors, &mut output);
    }

    #[test]
    fn should_be_able_to_get_matrix_representation_of_the_neuron_connections(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 1f64);
        organism.genome.inject_gene(1, 2, 0.5f64);
        organism.genome.inject_gene(2, 1, 0.5f64);
        organism.genome.inject_gene(2, 2, 0.75f64);
        organism.genome.inject_gene(1, 0, 1f64);
        assert_eq!(
            organism.get_weights_matrix(),
                (3, 3, vec![
                        0.0, 1.0, 0.0,
                        1.0, 0.0, 0.5,
                        0.0, 0.5, 0.75
                    ])
        );
    }
}
<commit_msg>Fix genome generation for ctrnn<commit_after>use neat::*;

#[derive(Debug, Clone)]
pub struct Organism {
    pub genome: Genome,
    pub fitness: f64,
    pub neurons: Vec<Option<Neuron>>
}

const ACTIVATION_CYCLES: usize = 10;

impl Organism {
    pub fn new(genome: Genome) -> Organism {
        Organism { genome: genome, fitness: 0f64, neurons: vec![] }
    }

    pub fn mutate(&self) -> Organism {
        let mut new_genome = self.genome.clone();
        new_genome.mutate();
        Organism::new(new_genome)
    }

    pub fn mate(&self, other: &Organism) -> Organism {
        Organism::new(self.genome.mate(&other.genome, self.fitness < other.fitness))
    }

    pub fn activate_old(&mut self, sensors: &Vec<f64>, outputs: &mut Vec<f64>){
       if self.neurons.len() == 0 {
           self.generate_phenome();
       };

       for _n in 0..ACTIVATION_CYCLES {
           for neuron_id in 0..sensors.len() {
               if neuron_id < self.neurons.len() {
                   self.neurons[neuron_id].as_mut().unwrap().stimulate(sensors[neuron_id]);
               }
           }

           self.activate_neurons();
           self.propagate_signals();
       }

       //Take outputs from next neurons after sensors
       let sensors_len = sensors.len();
       for neuron_id in sensors_len..(sensors_len + outputs.len()){
           if neuron_id < self.neurons.len() {
               outputs[neuron_id - sensors_len] = self.neurons[neuron_id].as_ref().map_or(0f64, |neuron| neuron.potential());
           } else {
               outputs[neuron_id - sensors_len] = 0f64;
           }
       }

       for neuron in &mut self.neurons {
           if neuron.is_some() {
               neuron.as_mut().unwrap().relax();
           }
       }
    }

    pub fn activate(&mut self, sensors: &Vec<f64>, outputs: &mut Vec<f64>){

        let neurons_len = self.genome.len();
        let gamma = vec![0.0; neurons_len];
        let tau = vec![10.0; neurons_len];
        let theta = vec![0.0; neurons_len];
        let wik = vec![1.0; sensors.len() * neurons_len];
        let i = sensors.clone();
        let activations = Ctrnn::new().activate(30, &gamma, 1.0, &tau, &self.get_weights_matrix(), &theta, &(neurons_len, sensors.len(), wik), &i);
        if sensors.len() < neurons_len {
            let outputs_activations = activations.split_at(sensors.len()).1.to_vec();
            for n in 0..outputs_activations.len() {
                outputs[n] = outputs_activations[n];
            }
        }
    }

    fn activate_neurons(&mut self){
        for neuron_id in 0..self.neurons.len() {
            self.neurons[neuron_id].as_mut().unwrap().shot();
        }
    }

    fn propagate_signals(&mut self){
        for neuron_option in self.neurons.clone() {
            let neuron = neuron_option.unwrap();
            for connection in &neuron.connections {
               self.neurons[connection.0].as_mut().unwrap().stimulate(neuron.potential() * connection.1);
            }
        }
    }

    fn generate_phenome(&mut self){
        let neurons_to_generate = self.genome.len();

        self.neurons = vec![Some(Neuron::new()); neurons_to_generate as usize];

        for gene in self.genome.get_genes() {
            let mut neuron = self.neurons[gene.in_neuron_id].as_mut().unwrap();
            neuron.connections.push(Connection(gene.out_neuron_id, gene.weight));
        }
    }

    fn get_weights_matrix(&self) -> (usize, usize, Vec<f64>){
        let neurons_len = self.genome.len();
        let mut matrix = vec![0.0; neurons_len * neurons_len];
        for gene in self.genome.get_genes() {
            if gene.enabled {
                matrix[(gene.out_neuron_id * neurons_len) + gene.in_neuron_id] = gene.weight
            }
        }
        (neurons_len, neurons_len, matrix)
    }
}

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

    #[test]
    fn should_be_able_to_generate_his_phenome(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 0, 0.1f64);
        organism.genome.inject_gene(0, 1, 0.2f64);
        organism.genome.inject_gene(1, 0, 0.3f64);
        organism.genome.inject_gene(1, 1, 0.4f64);
        organism.generate_phenome();
        assert!(organism.neurons.len() == 2);
        let neuron0 = &organism.neurons[0].as_ref().unwrap();
        let connection00 = &neuron0.connections[0];
        assert!(connection00.0 == 0usize);
        assert!(connection00.1 == 0.1f64);
        let connection01 = &neuron0.connections[1];
        assert!(connection01.0 == 1usize);
        assert!(connection01.1 == 0.2f64);
        let neuron1 = &organism.neurons[1].as_ref().unwrap();
        let connection10 = &neuron1.connections[0];
        assert!(connection10.0 == 0usize);
        assert!(connection10.1 == 0.3f64);
        let connection11 = &neuron1.connections[1];
        assert!(connection11.0 == 1usize);
        assert!(connection11.1 == 0.4f64);
    }

    #[test]
    fn should_propagate_signal_without_hidden_layers(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 2f64);
        let sensors = vec![1f64];
        let mut output = vec![0f64];
        organism.activate(&sensors, &mut output);
        assert!(output[0] > 0f64, format!("{:?} is not bigger than 0.9", output[0]));

        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, -2f64);
        let sensors = vec![1f64];
        let mut output = vec![0f64];
        organism.activate(&sensors, &mut output);
        assert!(output[0] < 0.1f64, format!("{:?} is not smaller than 0.1", output[0]));
    }

    #[test]
    fn should_propagate_signal_over_hidden_layers(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 0f64); //disable connection
        organism.genome.inject_gene(0, 2, 1f64);
        organism.genome.inject_gene(2, 1, 1f64);
        let sensors = vec![0f64];
        let mut output = vec![0f64];
        organism.activate(&sensors, &mut output);
        assert!(output[0] > 0.9f64, format!("{:?} is not bigger than 0.9", output[0]));
    }

    #[test]
    fn should_work_with_cyclic_networks(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 1f64);
        organism.genome.inject_gene(1, 2, 1f64);
        organism.genome.inject_gene(2, 1, 1f64);
        let mut output = vec![0f64];
        organism.activate(&vec![1f64], &mut output);
        assert!(output[0] > 0.9, format!("{:?} is not bigger than 0.9", output[0]));

        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, -1f64);
        organism.genome.inject_gene(1, 2, -1f64);
        organism.genome.inject_gene(2, 1, -1f64);
        let mut output = vec![0f64];
        organism.activate(&vec![1f64], &mut output);
        assert!(output[0] < 0.1, format!("{:?} is not smaller than 0.1", output[0]));
    }

    #[test]
    fn activate_organims_sensor_without_enough_neurons_should_ignore_it(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 1f64);
        let sensors = vec![0f64,0f64,0f64];
        let mut output = vec![0f64];
        organism.activate(&sensors, &mut output);
    }

    #[test]
    fn should_be_able_to_get_matrix_representation_of_the_neuron_connections(){
        let mut organism = Organism::new(Genome::new());
        organism.genome.inject_gene(0, 1, 1f64);
        organism.genome.inject_gene(1, 2, 0.5f64);
        organism.genome.inject_gene(2, 1, 0.5f64);
        organism.genome.inject_gene(2, 2, 0.75f64);
        organism.genome.inject_gene(1, 0, 1f64);
        assert_eq!(
            organism.get_weights_matrix(),
                (3, 3, vec![
                        0.0, 1.0, 0.0,
                        1.0, 0.0, 0.5,
                        0.0, 0.5, 0.75
                    ])
        );
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, Mikhail Vorotilov
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use super::FloatType;
use std::error::Error;
use std::fmt;
/// Possible errors
#[derive(Debug,PartialEq)]
pub enum SearchError {
    /// The algorithm could not converge within the given number of iterations
    NoConvergency,
    /// Initial values do not bracket zero
    NoBracketing,
    /// The algorithm cannot continue from the point where the derivative is zero
    ZeroDerivative,
}

impl fmt::Display for SearchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self{
            SearchError::NoConvergency=>write!(f, "Convergency Error"),
            SearchError::NoBracketing=>write!(f, "Bracketing Error"),
            SearchError::ZeroDerivative=>write!(f, "Zero Derivative Error"),
        }
    }
}
impl Error for SearchError {
    fn description(&self) -> &str {
        match self{
            SearchError::NoConvergency=>"The algorithm could not converge within the given number of iterations",
            SearchError::NoBracketing=>"Initial values do not bracket zero",
            SearchError::ZeroDerivative=>"The algorithm cannot continue from the point where the derivative is zero",
        }
    }
}

/// The way to check if the algorithm has finished by either finding a root
/// or reaching the iteration limit.
pub trait Convergency<F: FloatType> {
    /// Return true if the given Y value is close enough to the zero
    fn is_root_found(&mut self, y: F) -> bool;
    /// Return true if given x values are close enough to each other
    fn is_converged(&mut self, x1: F, x2: F) -> bool;
    /// Return true if no more iterations desired
    fn is_iteration_limit_reached(&mut self, iter: usize) -> bool;
}

impl<F: FloatType> Convergency<F> for F {
    /// Return true if the given Y value is close enough to the zero
    fn is_root_found(&mut self, y: F) -> bool {
        y.abs() < self.abs()
    }
    /// Return true if given x values are close enough to each other
    fn is_converged(&mut self, x1: F, x2: F) -> bool {
        (x1 - x2).abs() < self.abs()
    }
    /// Return true if no more iterations desired
    fn is_iteration_limit_reached(&mut self, iter: usize) -> bool {
        iter >= 30
    }
}

pub mod newton_raphson;
pub mod brent;
pub mod secant;
pub mod regula_falsi;
pub mod polynom;

pub mod simple_convergency;
pub mod debug_convergency;
<commit_msg>Add types Sample and Interval.<commit_after>// Copyright (c) 2015, Mikhail Vorotilov
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice, this
//   list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

use super::FloatType;
use std::error::Error;
use std::fmt;
/// Possible errors
#[derive(Debug,PartialEq)]
pub enum SearchError {
    /// The algorithm could not converge within the given number of iterations
    NoConvergency,
    /// Initial values do not bracket zero
    NoBracketing,
    /// The algorithm cannot continue from the point where the derivative is zero
    ZeroDerivative,
}

impl fmt::Display for SearchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self{
            SearchError::NoConvergency=>write!(f, "Convergency Error"),
            SearchError::NoBracketing=>write!(f, "Bracketing Error"),
            SearchError::ZeroDerivative=>write!(f, "Zero Derivative Error"),
        }
    }
}
impl Error for SearchError {
    fn description(&self) -> &str {
        match self{
            SearchError::NoConvergency=>"The algorithm could not converge within the given number of iterations",
            SearchError::NoBracketing=>"Initial values do not bracket zero",
            SearchError::ZeroDerivative=>"The algorithm cannot continue from the point where the derivative is zero",
        }
    }
}

/// The way to check if the algorithm has finished by either finding a root
/// or reaching the iteration limit.
pub trait Convergency<F: FloatType> {
    /// Return true if the given Y value is close enough to the zero
    fn is_root_found(&mut self, y: F) -> bool;
    /// Return true if given x values are close enough to each other
    fn is_converged(&mut self, x1: F, x2: F) -> bool;
    /// Return true if no more iterations desired
    fn is_iteration_limit_reached(&mut self, iter: usize) -> bool;
}

impl<F: FloatType> Convergency<F> for F {
    /// Return true if the given Y value is close enough to the zero
    fn is_root_found(&mut self, y: F) -> bool {
        y.abs() < self.abs()
    }
    /// Return true if given x values are close enough to each other
    fn is_converged(&mut self, x1: F, x2: F) -> bool {
        (x1 - x2).abs() < self.abs()
    }
    /// Return true if no more iterations desired
    fn is_iteration_limit_reached(&mut self, iter: usize) -> bool {
        iter >= 30
    }
}

pub mod newton_raphson;
pub mod brent;
pub mod secant;
pub mod regula_falsi;
pub mod polynom;

pub mod simple_convergency;
pub mod debug_convergency;
<|endoftext|>


<commit_before>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::block::Block;
use anyhow::ensure;
use aptos_crypto::hash::HashValue;
use aptos_types::validator_verifier::ValidatorVerifier;
use serde::{Deserialize, Serialize};
use short_hex_str::AsShortHexStr;
use std::fmt;

// this number is recommended to be greater than backpressure limit
// (so block retrievals can be done in a single request)
// but it should not be too large as the response size is bounded.
// TODO: add a test
pub const MAX_BLOCKS_PER_REQUEST: u64 = 10;

pub const MAX_FAILED_ATTEMPTS: u32 = 4;

/// RPC to get a chain of block of the given length starting from the given block id.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct BlockRetrievalRequest {
    block_id: HashValue,
    num_blocks: u64,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    target_block_id: Option<HashValue>,
}

impl BlockRetrievalRequest {
    pub fn new(block_id: HashValue, num_blocks: u64) -> Self {
        Self {
            block_id,
            num_blocks,
            target_block_id: None,
        }
    }
    pub fn new_with_target_block_id(
        block_id: HashValue,
        num_blocks: u64,
        target_block_id: HashValue,
    ) -> Self {
        Self {
            block_id,
            num_blocks,
            target_block_id: Some(target_block_id),
        }
    }
    pub fn block_id(&self) -> HashValue {
        self.block_id
    }
    pub fn num_blocks(&self) -> u64 {
        self.num_blocks
    }
    pub fn target_block_id(&self) -> Option<HashValue> {
        self.target_block_id
    }
    pub fn match_target_id(&self, hash_value: HashValue) -> bool {
        self.target_block_id.map_or(false, |id| id == hash_value)
    }
}

impl fmt::Display for BlockRetrievalRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "[BlockRetrievalRequest starting from id {} with {} blocks]",
            self.block_id, self.num_blocks
        )
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum BlockRetrievalStatus {
    // Successfully fill in the request.
    Succeeded,
    // Can not find the block corresponding to block_id.
    IdNotFound,
    // Can not find enough blocks but find some.
    NotEnoughBlocks,
    // Successfully found the target,
    SucceededWithTarget,
}

/// Carries the returned blocks and the retrieval status.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct BlockRetrievalResponse {
    status: BlockRetrievalStatus,
    blocks: Vec<Block>,
}

impl BlockRetrievalResponse {
    pub fn new(status: BlockRetrievalStatus, blocks: Vec<Block>) -> Self {
        Self { status, blocks }
    }

    pub fn status(&self) -> BlockRetrievalStatus {
        self.status.clone()
    }

    pub fn blocks(&self) -> &Vec<Block> {
        &self.blocks
    }

    pub fn verify(
        &self,
        retrieval_request: BlockRetrievalRequest,
        sig_verifier: &ValidatorVerifier,
    ) -> anyhow::Result<()> {
        ensure!(
            self.status != BlockRetrievalStatus::Succeeded
                || self.blocks.len() as u64 == retrieval_request.num_blocks(),
            "not enough blocks returned, expect {}, get {}",
            retrieval_request.num_blocks(),
            self.blocks.len(),
        );
        ensure!(
            self.status != BlockRetrievalStatus::SucceededWithTarget
                || (!self.blocks.is_empty()
                    && retrieval_request.match_target_id(self.blocks.last().unwrap().id())),
            "target not found in blocks returned, expect {:?}",
            retrieval_request.target_block_id(),
        );
        self.blocks
            .iter()
            .try_fold(retrieval_request.block_id(), |expected_id, block| {
                block.validate_signature(sig_verifier)?;
                block.verify_well_formed()?;
                ensure!(
                    block.id() == expected_id,
                    "blocks doesn't form a chain: expect {}, get {}",
                    expected_id,
                    block.id()
                );
                Ok(block.parent_id())
            })
            .map(|_| ())
    }
}

impl fmt::Display for BlockRetrievalResponse {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.status() {
            BlockRetrievalStatus::Succeeded | BlockRetrievalStatus::SucceededWithTarget => {
                write!(
                    f,
                    "[BlockRetrievalResponse: status: {:?}, num_blocks: {}, block_ids: ",
                    self.status(),
                    self.blocks().len(),
                )?;

                f.debug_list()
                    .entries(self.blocks.iter().map(|b| b.id().short_str()))
                    .finish()?;

                write!(f, "]")
            }
            _ => write!(f, "[BlockRetrievalResponse: status: {:?}]", self.status()),
        }
    }
}
<commit_msg>[consensus] Remove skip_serializing_if from BlockRetreivalRequest (#4644)<commit_after>// Copyright (c) Aptos
// SPDX-License-Identifier: Apache-2.0

use crate::block::Block;
use anyhow::ensure;
use aptos_crypto::hash::HashValue;
use aptos_types::validator_verifier::ValidatorVerifier;
use serde::{Deserialize, Serialize};
use short_hex_str::AsShortHexStr;
use std::fmt;

// this number is recommended to be greater than backpressure limit
// (so block retrievals can be done in a single request)
// but it should not be too large as the response size is bounded.
// TODO: add a test
pub const MAX_BLOCKS_PER_REQUEST: u64 = 10;

pub const MAX_FAILED_ATTEMPTS: u32 = 4;

/// RPC to get a chain of block of the given length starting from the given block id.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct BlockRetrievalRequest {
    block_id: HashValue,
    num_blocks: u64,
    target_block_id: Option<HashValue>,
}

impl BlockRetrievalRequest {
    pub fn new(block_id: HashValue, num_blocks: u64) -> Self {
        Self {
            block_id,
            num_blocks,
            target_block_id: None,
        }
    }
    pub fn new_with_target_block_id(
        block_id: HashValue,
        num_blocks: u64,
        target_block_id: HashValue,
    ) -> Self {
        Self {
            block_id,
            num_blocks,
            target_block_id: Some(target_block_id),
        }
    }
    pub fn block_id(&self) -> HashValue {
        self.block_id
    }
    pub fn num_blocks(&self) -> u64 {
        self.num_blocks
    }
    pub fn target_block_id(&self) -> Option<HashValue> {
        self.target_block_id
    }
    pub fn match_target_id(&self, hash_value: HashValue) -> bool {
        self.target_block_id.map_or(false, |id| id == hash_value)
    }
}

impl fmt::Display for BlockRetrievalRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "[BlockRetrievalRequest starting from id {} with {} blocks]",
            self.block_id, self.num_blocks
        )
    }
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub enum BlockRetrievalStatus {
    // Successfully fill in the request.
    Succeeded,
    // Can not find the block corresponding to block_id.
    IdNotFound,
    // Can not find enough blocks but find some.
    NotEnoughBlocks,
    // Successfully found the target,
    SucceededWithTarget,
}

/// Carries the returned blocks and the retrieval status.
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct BlockRetrievalResponse {
    status: BlockRetrievalStatus,
    blocks: Vec<Block>,
}

impl BlockRetrievalResponse {
    pub fn new(status: BlockRetrievalStatus, blocks: Vec<Block>) -> Self {
        Self { status, blocks }
    }

    pub fn status(&self) -> BlockRetrievalStatus {
        self.status.clone()
    }

    pub fn blocks(&self) -> &Vec<Block> {
        &self.blocks
    }

    pub fn verify(
        &self,
        retrieval_request: BlockRetrievalRequest,
        sig_verifier: &ValidatorVerifier,
    ) -> anyhow::Result<()> {
        ensure!(
            self.status != BlockRetrievalStatus::Succeeded
                || self.blocks.len() as u64 == retrieval_request.num_blocks(),
            "not enough blocks returned, expect {}, get {}",
            retrieval_request.num_blocks(),
            self.blocks.len(),
        );
        ensure!(
            self.status != BlockRetrievalStatus::SucceededWithTarget
                || (!self.blocks.is_empty()
                    && retrieval_request.match_target_id(self.blocks.last().unwrap().id())),
            "target not found in blocks returned, expect {:?}",
            retrieval_request.target_block_id(),
        );
        self.blocks
            .iter()
            .try_fold(retrieval_request.block_id(), |expected_id, block| {
                block.validate_signature(sig_verifier)?;
                block.verify_well_formed()?;
                ensure!(
                    block.id() == expected_id,
                    "blocks doesn't form a chain: expect {}, get {}",
                    expected_id,
                    block.id()
                );
                Ok(block.parent_id())
            })
            .map(|_| ())
    }
}

impl fmt::Display for BlockRetrievalResponse {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.status() {
            BlockRetrievalStatus::Succeeded | BlockRetrievalStatus::SucceededWithTarget => {
                write!(
                    f,
                    "[BlockRetrievalResponse: status: {:?}, num_blocks: {}, block_ids: ",
                    self.status(),
                    self.blocks().len(),
                )?;

                f.debug_list()
                    .entries(self.blocks.iter().map(|b| b.id().short_str()))
                    .finish()?;

                write!(f, "]")
            }
            _ => write!(f, "[BlockRetrievalResponse: status: {:?}]", self.status()),
        }
    }
}
<|endoftext|>


<commit_before>use {Model, User};
use util::{RequestUtils, CargoResult, ChainError, human};
use db::Connection;
use pg::rows::Row;
use util::errors::NotFound;
use http;
use app::App;

#[repr(u32)]
pub enum OwnerKind {
    User = 0,
    Team = 1,
}

/// Unifies the notion of a User or a Team.
pub enum Owner {
    User(User),
    Team(Team),
}

/// For now, just a Github Team. Can be upgraded to other teams
/// later if desirable.
pub struct Team {
    /// We're assuming these are stable
    pub github_id: i32,
    /// Unique table id
    pub id: i32,
    /// "github:org:team"
    /// An opaque unique ID, that was at one point parsed out to query Github.
    /// We only query membership with github using the github_id, though.
    /// This is the only name we should ever talk to Cargo about.
    pub login: String,
    /// Sugary goodness
    pub name: Option<String>,
    pub avatar: Option<String>,

}

#[derive(RustcEncodable)]
pub struct EncodableOwner {
    pub id: i32,
    pub login: String,
    pub kind: String,
    pub email: Option<String>,
    pub url: Option<String>,
    pub name: Option<String>,
    pub avatar: Option<String>,
}

/// Access rights to the crate (publishing and ownership management)
/// NOTE: The order of these variants matters!
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Rights {
    None,
    Publish,
    Full,
}

impl Team {
    /// Just gets the Team from the database by name.
    pub fn find_by_login(conn: &Connection, login: &str) -> CargoResult<Self> {
        let stmt = try!(conn.prepare("SELECT * FROM teams
                                      WHERE login = $1"));
        let rows = try!(stmt.query(&[&login]));
        let row = try!(rows.iter().next().chain_error(|| {
            NotFound
        }));
        Ok(Model::from_row(&row))
    }

    /// Tries to create the Team in the DB (assumes a `:` has already been found).
    pub fn create(app: &App, conn: &Connection, login: &str, req_user: &User)
                                                    -> CargoResult<Self> {
        // must look like system:xxxxxxx
        let mut chunks = login.split(":");
        match chunks.next().unwrap() {
            // github:rust-lang:owners
            "github" => {
                // Ok to unwrap since we know one ":" is contained
                let org = chunks.next().unwrap();
                let team = try!(chunks.next().ok_or_else(||
                    human("missing github team argument; \
                            format is github:org:team")
                ));
                Team::create_github_team(app, conn, login, org, team, req_user)
            }
            _ => {
                Err(human("unknown organization handler, \
                            only 'github:org:team' is supported"))
            }
        }
    }

    /// Tries to create a Github Team from scratch. Assumes `org` and `team` are
    /// correctly parsed out of the full `name`. `name` is passed as a
    /// convenience to avoid rebuilding it.
    pub fn create_github_team(app: &App, conn: &Connection, login: &str,
                              org_name: &str, team_name: &str, req_user: &User)
                              -> CargoResult<Self> {
        // GET orgs/:org/teams
        // check that `team` is the `slug` in results, and grab its data

        // "sanitization"
        fn whitelist(c: &char) -> bool {
            match *c {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => false,
                _ => true
            }
        }

        if let Some(c) = org_name.chars().find(whitelist) {
            return Err(human(format!("organization cannot contain special \
                                        characters like {}", c)));
        }

        #[derive(RustcDecodable)]
        struct GithubTeam {
            slug: String,   // the name we want to find
            id: i32,        // unique GH id (needed for membership queries)
            name: Option<String>,   // Pretty name
        }

        // FIXME: we just set per_page=100 and don't bother chasing pagination
        // links. A hundred teams should be enough for any org, right?
        let url = format!("/orgs/{}/teams", org_name);
        let token = http::token(req_user.gh_access_token.clone());
        let resp = try!(http::github(app, &url, &token));
        let teams: Vec<GithubTeam> = try!(http::parse_github_response(resp));

        let team = try!(teams.into_iter().find(|team| team.slug == team_name)
            .ok_or_else(||{
                human(format!("could not find the github team {}/{}",
                            org_name, team_name))
            })
        );

        if !try!(team_with_gh_id_contains_user(app, team.id, req_user)) {
            return Err(human("only members of a team can add it as an owner"));
        }

        #[derive(RustcDecodable)]
        struct Org {
            avatar_url: Option<String>,
        }

        let url = format!("/orgs/{}", org_name);
        let resp = try!(http::github(app, &url, &token));
        let org: Org = try!(http::parse_github_response(resp));

        Team::insert(conn, login, team.id, team.name, org.avatar_url)
    }

    pub fn insert(conn: &Connection,
                  login: &str,
                  github_id: i32,
                  name: Option<String>,
                  avatar: Option<String>)
                  -> CargoResult<Self> {

        let stmt = try!(conn.prepare("INSERT INTO teams
                                   (login, github_id, name, avatar)
                                   VALUES ($1, $2, $3, $4)
                                   RETURNING *"));

        let rows = try!(stmt.query(&[&login, &github_id, &name, &avatar]));
        let row = rows.iter().next().unwrap();
        Ok(Model::from_row(&row))
    }

    /// Phones home to Github to ask if this User is a member of the given team.
    /// Note that we're assuming that the given user is the one interested in
    /// the answer. If this is not the case, then we could accidentally leak
    /// private membership information here.
    pub fn contains_user(&self, app: &App, user: &User) -> CargoResult<bool> {
        team_with_gh_id_contains_user(app, self.github_id, user)
    }
}

fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User)
                                                -> CargoResult<bool> {
    // GET teams/:team_id/memberships/:user_name
    // check that "state": "active"

    #[derive(RustcDecodable)]
    struct Membership {
        state: String,
    }

    let url = format!("/teams/{}/memberships/{}",
                        &github_id, &user.gh_login);
    let token = http::token(user.gh_access_token.clone());
    let resp = try!(http::github(app, &url, &token));

    // Officially how `false` is returned
    if resp.get_code() == 404 { return Ok(false) }

    let membership: Membership = try!(http::parse_github_response(resp));

    // There is also `state: pending` for which we could possibly give
    // some feedback, but it's not obvious how that should work.
    Ok(membership.state == "active")
}

impl Model for Team {
    fn from_row(row: &Row) -> Self {
        Team {
            id: row.get("id"),
            name: row.get("name"),
            github_id: row.get("github_id"),
            login: row.get("login"),
            avatar: row.get("avatar"),
        }
    }

    fn table_name(_: Option<Self>) -> &'static str { "teams" }
}

impl Owner {
    /// Finds the owner by name, failing out if it doesn't exist.
    /// May be a user's GH login, or a full team name. This is case
    /// sensitive.
    pub fn find_by_login(conn: &Connection, name: &str) -> CargoResult<Owner> {
        let owner = if name.contains(":") {
            Owner::Team(try!(Team::find_by_login(conn, name).map_err(|_|
                human(format!("could not find team with name {}", name))
            )))
        } else {
            Owner::User(try!(User::find_by_login(conn, name).map_err(|_|
                human(format!("could not find user with login `{}`", name))
            )))
        };
        Ok(owner)
    }

    pub fn kind(&self) -> i32 {
        match *self {
            Owner::User(_) => OwnerKind::User as i32,
            Owner::Team(_) => OwnerKind::Team as i32,
        }
    }

    pub fn login(&self) -> &str {
        match *self {
            Owner::User(ref user) => &user.gh_login,
            Owner::Team(ref team) => &team.login,
        }
    }

    pub fn id(&self) -> i32 {
        match *self {
            Owner::User(ref user) => user.id,
            Owner::Team(ref team) => team.id,
        }
    }

    pub fn encodable(self) -> EncodableOwner {
        match self {
            Owner::User(User { id, email, name, gh_login, avatar, .. }) => {
                let url = format!("https://github.com/{}", gh_login);
                EncodableOwner {
                    id: id,
                    login: gh_login,
                    email: email,
                    avatar: avatar,
                    url: Some(url),
                    name: name,
                    kind: String::from("user"),
                }
            }
            Owner::Team(Team { id, name, login, avatar, .. }) => {
                let url = {
                    let mut parts = login.split(":");
                    parts.next(); // discard github
                    format!("https://github.com/{}/teams/{}",
                            parts.next().unwrap(), parts.next().unwrap())
                };
                EncodableOwner {
                    id: id,
                    login: login,
                    email: None,
                    url: Some(url),
                    avatar: avatar,
                    name: name,
                    kind: String::from("team"),
                }
            }
        }
    }
}

/// Given this set of owners, determines the strongest rights the
/// given user has.
///
/// Shortcircuits on `Full` because you can't beat it. In practice we'll always
/// see `[user, user, user, ..., team, team, team]`, so we could shortcircuit on
/// `Publish` as well, but this is a non-obvious invariant so we don't bother.
/// Sweet free optimization if teams are proving burdensome to check.
/// More than one team isn't really expected, though.
pub fn rights(app: &App, owners: &[Owner], user: &User) -> CargoResult<Rights> {
    let mut best = Rights::None;
    for owner in owners {
        match *owner {
            Owner::User(ref other_user) => if other_user.id == user.id {
                return Ok(Rights::Full);
            },
            Owner::Team(ref team) => if try!(team.contains_user(app, user)) {
                best = Rights::Publish;
            },
        }
    }
    Ok(best)
}

<commit_msg>Read lots of teams per page<commit_after>use {Model, User};
use util::{RequestUtils, CargoResult, ChainError, human};
use db::Connection;
use pg::rows::Row;
use util::errors::NotFound;
use http;
use app::App;

#[repr(u32)]
pub enum OwnerKind {
    User = 0,
    Team = 1,
}

/// Unifies the notion of a User or a Team.
pub enum Owner {
    User(User),
    Team(Team),
}

/// For now, just a Github Team. Can be upgraded to other teams
/// later if desirable.
pub struct Team {
    /// We're assuming these are stable
    pub github_id: i32,
    /// Unique table id
    pub id: i32,
    /// "github:org:team"
    /// An opaque unique ID, that was at one point parsed out to query Github.
    /// We only query membership with github using the github_id, though.
    /// This is the only name we should ever talk to Cargo about.
    pub login: String,
    /// Sugary goodness
    pub name: Option<String>,
    pub avatar: Option<String>,

}

#[derive(RustcEncodable)]
pub struct EncodableOwner {
    pub id: i32,
    pub login: String,
    pub kind: String,
    pub email: Option<String>,
    pub url: Option<String>,
    pub name: Option<String>,
    pub avatar: Option<String>,
}

/// Access rights to the crate (publishing and ownership management)
/// NOTE: The order of these variants matters!
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Rights {
    None,
    Publish,
    Full,
}

impl Team {
    /// Just gets the Team from the database by name.
    pub fn find_by_login(conn: &Connection, login: &str) -> CargoResult<Self> {
        let stmt = try!(conn.prepare("SELECT * FROM teams
                                      WHERE login = $1"));
        let rows = try!(stmt.query(&[&login]));
        let row = try!(rows.iter().next().chain_error(|| {
            NotFound
        }));
        Ok(Model::from_row(&row))
    }

    /// Tries to create the Team in the DB (assumes a `:` has already been found).
    pub fn create(app: &App, conn: &Connection, login: &str, req_user: &User)
                                                    -> CargoResult<Self> {
        // must look like system:xxxxxxx
        let mut chunks = login.split(":");
        match chunks.next().unwrap() {
            // github:rust-lang:owners
            "github" => {
                // Ok to unwrap since we know one ":" is contained
                let org = chunks.next().unwrap();
                let team = try!(chunks.next().ok_or_else(||
                    human("missing github team argument; \
                            format is github:org:team")
                ));
                Team::create_github_team(app, conn, login, org, team, req_user)
            }
            _ => {
                Err(human("unknown organization handler, \
                            only 'github:org:team' is supported"))
            }
        }
    }

    /// Tries to create a Github Team from scratch. Assumes `org` and `team` are
    /// correctly parsed out of the full `name`. `name` is passed as a
    /// convenience to avoid rebuilding it.
    pub fn create_github_team(app: &App, conn: &Connection, login: &str,
                              org_name: &str, team_name: &str, req_user: &User)
                              -> CargoResult<Self> {
        // GET orgs/:org/teams
        // check that `team` is the `slug` in results, and grab its data

        // "sanitization"
        fn whitelist(c: &char) -> bool {
            match *c {
                'a'...'z' | 'A'...'Z' | '0'...'9' | '-' | '_' => false,
                _ => true
            }
        }

        if let Some(c) = org_name.chars().find(whitelist) {
            return Err(human(format!("organization cannot contain special \
                                        characters like {}", c)));
        }

        #[derive(RustcDecodable)]
        struct GithubTeam {
            slug: String,   // the name we want to find
            id: i32,        // unique GH id (needed for membership queries)
            name: Option<String>,   // Pretty name
        }

        // FIXME: we just set per_page=100 and don't bother chasing pagination
        // links. A hundred teams should be enough for any org, right?
        let url = format!("/orgs/{}/teams?per_page=100", org_name);
        let token = http::token(req_user.gh_access_token.clone());
        let resp = try!(http::github(app, &url, &token));
        let teams: Vec<GithubTeam> = try!(http::parse_github_response(resp));

        let team = try!(teams.into_iter().find(|team| team.slug == team_name)
            .ok_or_else(||{
                human(format!("could not find the github team {}/{}",
                            org_name, team_name))
            })
        );

        if !try!(team_with_gh_id_contains_user(app, team.id, req_user)) {
            return Err(human("only members of a team can add it as an owner"));
        }

        #[derive(RustcDecodable)]
        struct Org {
            avatar_url: Option<String>,
        }

        let url = format!("/orgs/{}", org_name);
        let resp = try!(http::github(app, &url, &token));
        let org: Org = try!(http::parse_github_response(resp));

        Team::insert(conn, login, team.id, team.name, org.avatar_url)
    }

    pub fn insert(conn: &Connection,
                  login: &str,
                  github_id: i32,
                  name: Option<String>,
                  avatar: Option<String>)
                  -> CargoResult<Self> {

        let stmt = try!(conn.prepare("INSERT INTO teams
                                   (login, github_id, name, avatar)
                                   VALUES ($1, $2, $3, $4)
                                   RETURNING *"));

        let rows = try!(stmt.query(&[&login, &github_id, &name, &avatar]));
        let row = rows.iter().next().unwrap();
        Ok(Model::from_row(&row))
    }

    /// Phones home to Github to ask if this User is a member of the given team.
    /// Note that we're assuming that the given user is the one interested in
    /// the answer. If this is not the case, then we could accidentally leak
    /// private membership information here.
    pub fn contains_user(&self, app: &App, user: &User) -> CargoResult<bool> {
        team_with_gh_id_contains_user(app, self.github_id, user)
    }
}

fn team_with_gh_id_contains_user(app: &App, github_id: i32, user: &User)
                                                -> CargoResult<bool> {
    // GET teams/:team_id/memberships/:user_name
    // check that "state": "active"

    #[derive(RustcDecodable)]
    struct Membership {
        state: String,
    }

    let url = format!("/teams/{}/memberships/{}",
                        &github_id, &user.gh_login);
    let token = http::token(user.gh_access_token.clone());
    let resp = try!(http::github(app, &url, &token));

    // Officially how `false` is returned
    if resp.get_code() == 404 { return Ok(false) }

    let membership: Membership = try!(http::parse_github_response(resp));

    // There is also `state: pending` for which we could possibly give
    // some feedback, but it's not obvious how that should work.
    Ok(membership.state == "active")
}

impl Model for Team {
    fn from_row(row: &Row) -> Self {
        Team {
            id: row.get("id"),
            name: row.get("name"),
            github_id: row.get("github_id"),
            login: row.get("login"),
            avatar: row.get("avatar"),
        }
    }

    fn table_name(_: Option<Self>) -> &'static str { "teams" }
}

impl Owner {
    /// Finds the owner by name, failing out if it doesn't exist.
    /// May be a user's GH login, or a full team name. This is case
    /// sensitive.
    pub fn find_by_login(conn: &Connection, name: &str) -> CargoResult<Owner> {
        let owner = if name.contains(":") {
            Owner::Team(try!(Team::find_by_login(conn, name).map_err(|_|
                human(format!("could not find team with name {}", name))
            )))
        } else {
            Owner::User(try!(User::find_by_login(conn, name).map_err(|_|
                human(format!("could not find user with login `{}`", name))
            )))
        };
        Ok(owner)
    }

    pub fn kind(&self) -> i32 {
        match *self {
            Owner::User(_) => OwnerKind::User as i32,
            Owner::Team(_) => OwnerKind::Team as i32,
        }
    }

    pub fn login(&self) -> &str {
        match *self {
            Owner::User(ref user) => &user.gh_login,
            Owner::Team(ref team) => &team.login,
        }
    }

    pub fn id(&self) -> i32 {
        match *self {
            Owner::User(ref user) => user.id,
            Owner::Team(ref team) => team.id,
        }
    }

    pub fn encodable(self) -> EncodableOwner {
        match self {
            Owner::User(User { id, email, name, gh_login, avatar, .. }) => {
                let url = format!("https://github.com/{}", gh_login);
                EncodableOwner {
                    id: id,
                    login: gh_login,
                    email: email,
                    avatar: avatar,
                    url: Some(url),
                    name: name,
                    kind: String::from("user"),
                }
            }
            Owner::Team(Team { id, name, login, avatar, .. }) => {
                let url = {
                    let mut parts = login.split(":");
                    parts.next(); // discard github
                    format!("https://github.com/{}/teams/{}",
                            parts.next().unwrap(), parts.next().unwrap())
                };
                EncodableOwner {
                    id: id,
                    login: login,
                    email: None,
                    url: Some(url),
                    avatar: avatar,
                    name: name,
                    kind: String::from("team"),
                }
            }
        }
    }
}

/// Given this set of owners, determines the strongest rights the
/// given user has.
///
/// Shortcircuits on `Full` because you can't beat it. In practice we'll always
/// see `[user, user, user, ..., team, team, team]`, so we could shortcircuit on
/// `Publish` as well, but this is a non-obvious invariant so we don't bother.
/// Sweet free optimization if teams are proving burdensome to check.
/// More than one team isn't really expected, though.
pub fn rights(app: &App, owners: &[Owner], user: &User) -> CargoResult<Rights> {
    let mut best = Rights::None;
    for owner in owners {
        match *owner {
            Owner::User(ref other_user) => if other_user.id == user.id {
                return Ok(Rights::Full);
            },
            Owner::Team(ref team) => if try!(team.contains_user(app, user)) {
                best = Rights::Publish;
            },
        }
    }
    Ok(best)
}

<|endoftext|>