<commit_before>// Copyright (c) 2015-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for sending and receiving data link layer packets using the netmap library

#![allow(bad_style)]

extern crate netmap_sys;
extern crate libc;

use datalink::{self, NetworkInterface};
use datalink::{EthernetDataLinkChannelIterator, EthernetDataLinkReceiver, EthernetDataLinkSender};
use datalink::Channel::Ethernet;
use packet::Packet;
use packet::ethernet::{EthernetPacket, MutableEthernetPacket};
use self::netmap_sys::netmap::{netmap_slot, nm_ring_empty};
use self::netmap_sys::netmap_user::{NETMAP_BUF, NETMAP_FD, NETMAP_TXRING, nm_close, nm_desc,
                                    nm_nextpkt, nm_open, nm_pkthdr, nm_ring_next};

use std::ffi::CString;
use std::fs::File;
use std::io;
use std::io::Read;
use std::mem;
use std::path::Path;
use std::ptr;
use std::slice;
use std::sync::Arc;
use std::time::Duration;

#[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[repr(C)]
struct pollfd {
    fd: libc::c_int,
    events: libc::c_short,
    revents: libc::c_short,
}

#[cfg(any(target_os = "linux", target_os = "freebsd"))]
const POLLIN: libc::c_short = 0x0001;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
const POLLOUT: libc::c_short = 0x0004;

#[cfg(target_os = "freebsd")]
type nfds_t = libc::c_uint;
#[cfg(target_os = "linux")]
type nfds_t = libc::c_ulong;

extern "C" {
    fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: libc::c_int) -> libc::c_int;
    fn ppoll(fds: *mut pollfd,
             nfds: nfds_t,
             timeout: *const libc::timespec,
             newsigmask: *const libc::sigset_t)
        -> libc::c_int;
}

struct NmDesc {
    desc: *mut nm_desc,
    buf_size: libc::c_uint,
}

unsafe impl Send for NmDesc {}
unsafe impl Sync for NmDesc {}

impl NmDesc {
    fn new(iface: &NetworkInterface) -> io::Result<NmDesc> {
        let ifname = CString::new(("netmap:".to_owned() + &iface.name[..]).as_bytes());
        let desc = unsafe { nm_open(ifname.unwrap().as_ptr(), ptr::null(), 0, ptr::null()) };

        if desc.is_null() {
            Err(io::Error::last_os_error())
        } else {
            let mut f = try!(File::open(&Path::new("/sys/module/netmap/parameters/buf_size")));
            let mut num_str = String::new();
            try!(f.read_to_string(&mut num_str));
            let buf_size = num_str.trim_right().parse().unwrap();

            Ok(NmDesc {
                desc: desc,
                buf_size: buf_size,
            })
        }
    }
}

impl Drop for NmDesc {
    fn drop(&mut self) {
        unsafe {
            nm_close(self.desc);
        }
    }
}

/// Netmap specific configuration
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,

    /// The write timeout. Defaults to None.
    pub write_timeout: Option<Duration>,
}

impl<'a> From<&'a datalink::Config> for Config {
    fn from(config: &datalink::Config) -> Config {
        Config {
            read_timeout: config.read_timeout,
            write_timeout: config.write_timeout,
        }
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            read_timeout: None,
            write_timeout: None,
        }
    }
}

#[inline]
fn get_timeout(to: Option<Duration>) -> Option<libc::timespec> {
    to.map(|dur| {
        libc::timespec {
            tv_sec: dur.as_secs() as libc::time_t,
            tv_nsec: dur.subsec_nanos() as libc::c_long,
        }
    })
}

/// Create a datalink channel using the netmap library
#[inline]
pub fn channel(network_interface: &NetworkInterface,
               config: Config)
    -> io::Result<datalink::Channel> {
    // FIXME probably want one for each of send/recv
    let desc = NmDesc::new(network_interface);
    match desc {
        Ok(desc) => {
            let arc = Arc::new(desc);

            Ok(Ethernet(Box::new(DataLinkSenderImpl {
                            desc: arc.clone(),
                            timeout: get_timeout(config.write_timeout),
                        }),
                        Box::new(DataLinkReceiverImpl {
                            desc: arc,
                            timeout: get_timeout(config.read_timeout),
                        })))
        }
        Err(e) => Err(e),
    }
}

struct DataLinkSenderImpl {
    desc: Arc<NmDesc>,
    timeout: Option<libc::timespec>,
}

impl EthernetDataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(&mut self,
                      num_packets: usize,
                      packet_size: usize,
                      func: &mut FnMut(MutableEthernetPacket))
        -> Option<io::Result<()>> {
        assert!(packet_size <= self.desc.buf_size as usize);
        let desc = self.desc.desc;
        let mut fds = pollfd {
            fd: unsafe { NETMAP_FD(desc) },
            events: POLLOUT,
            revents: 0,
        };
        let mut packet_idx = 0usize;
        while packet_idx < num_packets {
            unsafe {
                let timespec =
                    self.timeout.as_ref().map(|ts| ts as *const _).unwrap_or(ptr::null());
                if ppoll(&mut fds, 1, timespec, ptr::null()) < 0 {
                    return Some(Err(io::Error::last_os_error()));
                }
                let ring = NETMAP_TXRING((*desc).nifp, 0);
                while !nm_ring_empty(ring) && packet_idx < num_packets {
                    let i = (*ring).cur;
                    let slot_ptr: *mut netmap_slot = mem::transmute(&mut (*ring).slot);
                    let buf = NETMAP_BUF(ring, (*slot_ptr.offset(i as isize)).buf_idx as isize);
                    let slice = slice::from_raw_parts_mut(buf as *mut u8, packet_size);
                    let meh = MutableEthernetPacket::new(slice).unwrap();
                    (*slot_ptr.offset(i as isize)).len = packet_size as u16;
                    func(meh);
                    let next = nm_ring_next(ring, i);
                    (*ring).head = next;
                    (*ring).cur = next;
                    packet_idx += 1;
                }
            }
        }

        Some(Ok(()))
    }

    #[inline]
    fn send_to(&mut self,
               packet: &EthernetPacket,
               _dst: Option<NetworkInterface>)
        -> Option<io::Result<()>> {
        use packet::MutablePacket;
        self.build_and_send(1,
                            packet.packet().len(),
                            &mut |mut eh: MutableEthernetPacket| {
                                eh.clone_from(packet);
                            })
    }
}

struct DataLinkReceiverImpl {
    desc: Arc<NmDesc>,
    timeout: Option<libc::timespec>,
}

impl EthernetDataLinkReceiver for DataLinkReceiverImpl {
    // FIXME Layer 3
    fn iter<'a>(&'a mut self) -> Box<EthernetDataLinkChannelIterator + 'a> {
        Box::new(DataLinkChannelIteratorImpl { pc: self })
    }
}

struct DataLinkChannelIteratorImpl<'a> {
    pc: &'a mut DataLinkReceiverImpl,
}

impl<'a> EthernetDataLinkChannelIterator<'a> for DataLinkChannelIteratorImpl<'a> {
    fn next(&mut self) -> io::Result<EthernetPacket> {
        let desc = self.pc.desc.desc;
        let mut h: nm_pkthdr = unsafe { mem::uninitialized() };
        let mut buf = unsafe { nm_nextpkt(desc, &mut h) };
        if buf.is_null() {
            let mut fds = pollfd {
                fd: unsafe { NETMAP_FD(desc) },
                events: POLLIN,
                revents: 0,
            };
            let timespec = self.pc.timeout.as_ref().map(|ts| ts as *const _).unwrap_or(ptr::null());
            if unsafe { ppoll(&mut fds, 1, timespec, ptr::null()) } < 0 {
                return Err(io::Error::last_os_error());
            }
            buf = unsafe { nm_nextpkt(desc, &mut h) };
        }
        Ok(EthernetPacket::new(unsafe { slice::from_raw_parts(buf, h.len as usize) }).unwrap())
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    #[path = "unix_interfaces.rs"]
    mod interfaces;
    interfaces::interfaces()
}
<commit_msg>Adapt netmap backend to &[u8] datalink<commit_after>// Copyright (c) 2015-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for sending and receiving data link layer packets using the netmap library

#![allow(bad_style)]

extern crate netmap_sys;
extern crate libc;

use datalink::{self, NetworkInterface};
use datalink::{EthernetDataLinkChannelIterator, EthernetDataLinkReceiver, EthernetDataLinkSender};
use datalink::Channel::Ethernet;
use self::netmap_sys::netmap::{netmap_slot, nm_ring_empty};
use self::netmap_sys::netmap_user::{NETMAP_BUF, NETMAP_FD, NETMAP_TXRING, nm_close, nm_desc,
                                    nm_nextpkt, nm_open, nm_pkthdr, nm_ring_next};

use std::ffi::CString;
use std::fs::File;
use std::io;
use std::io::Read;
use std::mem;
use std::path::Path;
use std::ptr;
use std::slice;
use std::sync::Arc;
use std::time::Duration;

#[cfg(any(target_os = "linux", target_os = "freebsd"))]
#[repr(C)]
struct pollfd {
    fd: libc::c_int,
    events: libc::c_short,
    revents: libc::c_short,
}

#[cfg(any(target_os = "linux", target_os = "freebsd"))]
const POLLIN: libc::c_short = 0x0001;
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
const POLLOUT: libc::c_short = 0x0004;

#[cfg(target_os = "freebsd")]
type nfds_t = libc::c_uint;
#[cfg(target_os = "linux")]
type nfds_t = libc::c_ulong;

extern "C" {
    fn poll(fds: *mut pollfd, nfds: nfds_t, timeout: libc::c_int) -> libc::c_int;
    fn ppoll(fds: *mut pollfd,
             nfds: nfds_t,
             timeout: *const libc::timespec,
             newsigmask: *const libc::sigset_t)
        -> libc::c_int;
}

struct NmDesc {
    desc: *mut nm_desc,
    buf_size: libc::c_uint,
}

unsafe impl Send for NmDesc {}
unsafe impl Sync for NmDesc {}

impl NmDesc {
    fn new(iface: &NetworkInterface) -> io::Result<NmDesc> {
        let ifname = CString::new(("netmap:".to_owned() + &iface.name[..]).as_bytes());
        let desc = unsafe { nm_open(ifname.unwrap().as_ptr(), ptr::null(), 0, ptr::null()) };

        if desc.is_null() {
            Err(io::Error::last_os_error())
        } else {
            let mut f = try!(File::open(&Path::new("/sys/module/netmap/parameters/buf_size")));
            let mut num_str = String::new();
            try!(f.read_to_string(&mut num_str));
            let buf_size = num_str.trim_right().parse().unwrap();

            Ok(NmDesc {
                desc: desc,
                buf_size: buf_size,
            })
        }
    }
}

impl Drop for NmDesc {
    fn drop(&mut self) {
        unsafe {
            nm_close(self.desc);
        }
    }
}

/// Netmap specific configuration
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The read timeout. Defaults to None.
    pub read_timeout: Option<Duration>,

    /// The write timeout. Defaults to None.
    pub write_timeout: Option<Duration>,
}

impl<'a> From<&'a datalink::Config> for Config {
    fn from(config: &datalink::Config) -> Config {
        Config {
            read_timeout: config.read_timeout,
            write_timeout: config.write_timeout,
        }
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            read_timeout: None,
            write_timeout: None,
        }
    }
}

#[inline]
fn get_timeout(to: Option<Duration>) -> Option<libc::timespec> {
    to.map(|dur| {
        libc::timespec {
            tv_sec: dur.as_secs() as libc::time_t,
            tv_nsec: dur.subsec_nanos() as libc::c_long,
        }
    })
}

/// Create a datalink channel using the netmap library
#[inline]
pub fn channel(network_interface: &NetworkInterface,
               config: Config)
    -> io::Result<datalink::Channel> {
    // FIXME probably want one for each of send/recv
    let desc = NmDesc::new(network_interface);
    match desc {
        Ok(desc) => {
            let arc = Arc::new(desc);

            Ok(Ethernet(Box::new(DataLinkSenderImpl {
                            desc: arc.clone(),
                            timeout: get_timeout(config.write_timeout),
                        }),
                        Box::new(DataLinkReceiverImpl {
                            desc: arc,
                            timeout: get_timeout(config.read_timeout),
                        })))
        }
        Err(e) => Err(e),
    }
}

struct DataLinkSenderImpl {
    desc: Arc<NmDesc>,
    timeout: Option<libc::timespec>,
}

impl EthernetDataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(&mut self,
                      num_packets: usize,
                      packet_size: usize,
                      func: &mut FnMut(&mut [u8]))
        -> Option<io::Result<()>> {
        assert!(packet_size <= self.desc.buf_size as usize);
        let desc = self.desc.desc;
        let mut fds = pollfd {
            fd: unsafe { NETMAP_FD(desc) },
            events: POLLOUT,
            revents: 0,
        };
        let mut packet_idx = 0usize;
        while packet_idx < num_packets {
            unsafe {
                let timespec =
                    self.timeout.as_ref().map(|ts| ts as *const _).unwrap_or(ptr::null());
                if ppoll(&mut fds, 1, timespec, ptr::null()) < 0 {
                    return Some(Err(io::Error::last_os_error()));
                }
                let ring = NETMAP_TXRING((*desc).nifp, 0);
                while !nm_ring_empty(ring) && packet_idx < num_packets {
                    let i = (*ring).cur;
                    let slot_ptr: *mut netmap_slot = mem::transmute(&mut (*ring).slot);
                    let buf = NETMAP_BUF(ring, (*slot_ptr.offset(i as isize)).buf_idx as isize);
                    let slice = slice::from_raw_parts_mut(buf as *mut u8, packet_size);
                    (*slot_ptr.offset(i as isize)).len = packet_size as u16;
                    func(slice);
                    let next = nm_ring_next(ring, i);
                    (*ring).head = next;
                    (*ring).cur = next;
                    packet_idx += 1;
                }
            }
        }

        Some(Ok(()))
    }

    #[inline]
    fn send_to(&mut self,
               packet: &[u8],
               _dst: Option<NetworkInterface>)
        -> Option<io::Result<()>> {
        self.build_and_send(1,
                            packet.len(),
                            &mut |mut eh: &mut [u8]| {
                                eh.clone_from(packet);
                            })
    }
}

struct DataLinkReceiverImpl {
    desc: Arc<NmDesc>,
    timeout: Option<libc::timespec>,
}

impl EthernetDataLinkReceiver for DataLinkReceiverImpl {
    // FIXME Layer 3
    fn iter<'a>(&'a mut self) -> Box<EthernetDataLinkChannelIterator + 'a> {
        Box::new(DataLinkChannelIteratorImpl { pc: self })
    }
}

struct DataLinkChannelIteratorImpl<'a> {
    pc: &'a mut DataLinkReceiverImpl,
}

impl<'a> EthernetDataLinkChannelIterator<'a> for DataLinkChannelIteratorImpl<'a> {
    fn next(&mut self) -> io::Result<&[u8]> {
        let desc = self.pc.desc.desc;
        let mut h: nm_pkthdr = unsafe { mem::uninitialized() };
        let mut buf = unsafe { nm_nextpkt(desc, &mut h) };
        if buf.is_null() {
            let mut fds = pollfd {
                fd: unsafe { NETMAP_FD(desc) },
                events: POLLIN,
                revents: 0,
            };
            let timespec = self.pc.timeout.as_ref().map(|ts| ts as *const _).unwrap_or(ptr::null());
            if unsafe { ppoll(&mut fds, 1, timespec, ptr::null()) } < 0 {
                return Err(io::Error::last_os_error());
            }
            buf = unsafe { nm_nextpkt(desc, &mut h) };
        }
        Ok(unsafe { slice::from_raw_parts(buf, h.len as usize) })
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    #[path = "unix_interfaces.rs"]
    mod interfaces;
    interfaces::interfaces()
}
<|endoftext|>


<commit_before>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// 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/.

pub mod ffi;

use std::ffi::CString;
use std::io;

use domain::*;
use types::*;

use self::ffi::*;

pub struct LVar {
    subscriptions: Vec<Subscription>,
}

impl LVar {
    pub fn new() -> LVar {
        LVar { subscriptions: Vec::new() }
    }
}

impl Domain for LVar {
    fn write(&mut self, variable: &Var, value: &Value) -> io::Result<()> {
        debug!("processing a write operation for {:?} <- {}", variable, value);
        match variable {
            &Var::Named(ref lvar) => {
                let id = try!(check_named_variable(lvar).ok_or(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("there is no such named variable '{}'", lvar))));
                set_named_variable_value(id, f64::from(value));
                Ok(())
            }
            _ => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("lvar domain does not support variable {:?}", variable));
                Err(error)
            }
        }
    }

    fn subscribe(&mut self, device: DeviceId, variable: &Var) -> io::Result<()> {
        info!("receiving a subscription from device {} for {:?}", device, variable);
        match variable {
            &Var::Named(ref lvar) => {
                let subs = Subscription {
                    device: device,
                    lvar: lvar.clone(),
                    retain: None,
                };
                self.subscriptions.push(subs);
                Ok(())
            }
            _ => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("lvar domain does not support variable {:?}", variable));
                Err(error)
            }
        }
    }

    fn unsubscribe_all(&mut self, device: DeviceId) -> io::Result<()> {
        debug!("removing all subscriptions for device ID {}", device);
        self.subscriptions.retain(|s| s.device != device);
        Ok(())
    }

    fn poll(&mut self, events: &mut Vec<Event>) -> io::Result<()> {
        events.clear();
        for sub in self.subscriptions.iter_mut() {
            sub.trigger_event(events);
        }
        Ok(())
    }
}

struct Subscription {
    device: DeviceId,
    lvar: String,
    retain: Option<Value>,
}

impl Subscription {
    fn trigger_event(&mut self, events: &mut Vec<Event>) {
        let id = match check_named_variable(&self.lvar) {
            Some(id) => id,
            None => {
                error!("cannot obtain LVAR ID for variable {}", self.lvar);
                return;
            }
        };
        let val = Value::Number(get_named_variable_value(id) as isize);
        let must_trigger = self.retain.as_ref().map(|v| *v != val).unwrap_or(true);
        if must_trigger {
            let var = Var::Named(self.lvar.clone());
            let event = Event::new(self.device, "lvar", var, val);
            events.push(event);
        }
    }
}

fn check_named_variable(name: &str) -> Option<Id> {
    unsafe {
        let func = (*Panels).check_named_variable;
        let name = CString::new(name).unwrap();
        let id = (func)(name.as_ptr());
        if id != -1 { Some(id) } else { None }
    }
}

fn get_named_variable_value(id: Id) -> f64 {
    unsafe {
        let func = (*Panels).get_named_variable_value;
        (func)(id)
    }
}

fn set_named_variable_value(id: Id, value: f64) {
    unsafe {
        let func = (*Panels).set_named_variable_value;
        (func)(id, value)
    }
}
<commit_msg>Bug fixing: save retained value once LVAR event is polled<commit_after>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// 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/.

pub mod ffi;

use std::ffi::CString;
use std::io;

use domain::*;
use types::*;

use self::ffi::*;

pub struct LVar {
    subscriptions: Vec<Subscription>,
}

impl LVar {
    pub fn new() -> LVar {
        LVar { subscriptions: Vec::new() }
    }
}

impl Domain for LVar {
    fn write(&mut self, variable: &Var, value: &Value) -> io::Result<()> {
        debug!("processing a write operation for {:?} <- {}", variable, value);
        match variable {
            &Var::Named(ref lvar) => {
                let id = try!(check_named_variable(lvar).ok_or(io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("there is no such named variable '{}'", lvar))));
                set_named_variable_value(id, f64::from(value));
                Ok(())
            }
            _ => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("lvar domain does not support variable {:?}", variable));
                Err(error)
            }
        }
    }

    fn subscribe(&mut self, device: DeviceId, variable: &Var) -> io::Result<()> {
        info!("receiving a subscription from device {} for {:?}", device, variable);
        match variable {
            &Var::Named(ref lvar) => {
                let subs = Subscription {
                    device: device,
                    lvar: lvar.clone(),
                    retain: None,
                };
                self.subscriptions.push(subs);
                Ok(())
            }
            _ => {
                let error = io::Error::new(
                    io::ErrorKind::InvalidInput,
                    format!("lvar domain does not support variable {:?}", variable));
                Err(error)
            }
        }
    }

    fn unsubscribe_all(&mut self, device: DeviceId) -> io::Result<()> {
        debug!("removing all subscriptions for device ID {}", device);
        self.subscriptions.retain(|s| s.device != device);
        Ok(())
    }

    fn poll(&mut self, events: &mut Vec<Event>) -> io::Result<()> {
        events.clear();
        for sub in self.subscriptions.iter_mut() {
            sub.trigger_event(events);
        }
        Ok(())
    }
}

struct Subscription {
    device: DeviceId,
    lvar: String,
    retain: Option<Value>,
}

impl Subscription {
    fn trigger_event(&mut self, events: &mut Vec<Event>) {
        let id = match check_named_variable(&self.lvar) {
            Some(id) => id,
            None => {
                error!("cannot obtain LVAR ID for variable {}", self.lvar);
                return;
            }
        };
        let val = Value::Number(get_named_variable_value(id) as isize);
        let must_trigger = self.retain.as_ref().map(|v| *v != val).unwrap_or(true);
        if must_trigger {
            let var = Var::Named(self.lvar.clone());
            let event = Event::new(self.device, "lvar", var, val);
            events.push(event);
            self.retain = Some(val);
        }
    }
}

fn check_named_variable(name: &str) -> Option<Id> {
    unsafe {
        let func = (*Panels).check_named_variable;
        let name = CString::new(name).unwrap();
        let id = (func)(name.as_ptr());
        if id != -1 { Some(id) } else { None }
    }
}

fn get_named_variable_value(id: Id) -> f64 {
    unsafe {
        let func = (*Panels).get_named_variable_value;
        (func)(id)
    }
}

fn set_named_variable_value(id: Id, value: f64) {
    unsafe {
        let func = (*Panels).set_named_variable_value;
        (func)(id, value)
    }
}
<|endoftext|>


<commit_before>use crate::{
    app::{AppContext, AppContextPointer},
    errors::SondeError,
};
use gdk::Event;
use gtk::{
    self, prelude::*, Button, HeaderBar, IconSize, Notebook, Orientation, Paned, Separator, Widget,
    Window,
};
use std::rc::Rc;

mod menu_callbacks;

const TABS: [(&str, &str); 8] = [
    ("skew_t", "Skew T"),
    ("hodograph_area", "Hodograph"),
    ("fire_plume_container", "Fire Plume"),
    ("text_area_container", "Text"),
    ("control_area", "Controls"),
    ("profiles_area_container", "Profiles"),
    ("indexes_scrolled_window", "Indexes"),
    ("provider_data_text_container", "Provider Data"),
];

pub fn set_up_main_window(ac: &AppContextPointer) -> Result<(), SondeError> {
    connect_header_bar(ac)?;
    configure_main_window(ac)?;

    Ok(())
}

fn connect_header_bar(ac: &AppContextPointer) -> Result<(), SondeError> {
    let win: Window = ac.fetch_widget("main_window")?;
    let header_bar: HeaderBar = ac.fetch_widget("header-bar")?;

    let open_button = Button::new_from_icon_name(Some("document-open"), IconSize::SmallToolbar);
    open_button.set_label("Open");
    open_button.set_always_show_image(true);
    let ac1 = Rc::clone(ac);
    let win1 = win.clone();
    open_button.connect_clicked(move |_| {
        menu_callbacks::open_toolbar_callback(&ac1, &win1);
    });
    header_bar.pack_start(&open_button);

    let save_image_button =
        Button::new_from_icon_name(Some("insert-image"), IconSize::SmallToolbar);
    save_image_button.set_label("Save Image");
    save_image_button.set_always_show_image(true);
    let ac1 = Rc::clone(ac);
    let win1 = win.clone();
    save_image_button.connect_clicked(move |_| menu_callbacks::save_image_callback(&ac1, &win1));
    header_bar.pack_start(&save_image_button);

    header_bar.pack_start(&Separator::new(Orientation::Vertical));

    let first_button =
        Button::new_from_icon_name(Some("go-first"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    first_button.connect_clicked(move |_| ac1.display_first());
    header_bar.pack_start(&first_button);

    let previous_button =
        Button::new_from_icon_name(Some("media-skip-backward"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    previous_button.connect_clicked(move |_| ac1.display_previous());
    header_bar.pack_start(&previous_button);

    let next_button =
        Button::new_from_icon_name(Some("media-skip-forward"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    next_button.connect_clicked(move |_| ac1.display_next());
    header_bar.pack_start(&next_button);

    let last_button = Button::new_from_icon_name(Some("go-last"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    last_button.connect_clicked(move |_| ac1.display_last());
    header_bar.pack_start(&last_button);

    header_bar.pack_start(&Separator::new(Orientation::Vertical));

    let zoom_in_button = Button::new_from_icon_name(Some("zoom-in"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    zoom_in_button.connect_clicked(move |_| ac1.zoom_in());
    header_bar.pack_start(&zoom_in_button);

    let zoom_out_button = Button::new_from_icon_name(Some("zoom-out"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    zoom_out_button.connect_clicked(move |_| ac1.zoom_out());
    header_bar.pack_start(&zoom_out_button);

    let quit_button =
        Button::new_from_icon_name(Some("application-exit-symbolic"), IconSize::SmallToolbar);
    quit_button.connect_clicked(|_| {
        gtk::main_quit();
    });
    header_bar.pack_end(&quit_button);

    Ok(())
}

fn configure_main_window(ac: &AppContextPointer) -> Result<(), SondeError> {
    let window: Window = ac.fetch_widget("main_window")?;

    layout_tabs_window(&window, ac)?;

    let ac1 = Rc::clone(ac);
    window.connect_delete_event(move |win, ev| on_delete(win, ev, &ac1));

    window.show_all();

    Ok(())
}

fn on_delete(win: &Window, _ev: &Event, ac: &AppContext) -> Inhibit {
    let mut config = ac.config.borrow_mut();

    // Save the window dimensions
    let (width, height) = win.get_size();
    config.window_width = width;
    config.window_height = height;

    // Save the Paned view slider position.
    if let Ok(pane) = ac.fetch_widget::<Paned>("main_pane_view") {
        let pos = (pane.get_position() as f32) / (width as f32);
        config.pane_position = pos;
    }

    // Save the tabs, which notebook they are in, their order, and which ones were selected.
    if let (Ok(lnb), Ok(rnb)) = (
        ac.fetch_widget::<Notebook>("left_notebook"),
        ac.fetch_widget::<Notebook>("right_notebook"),
    ) {
        let tabs: Vec<Widget> = TABS
            .iter()
            // If there is an error here, it will ALWAYS fail. So go ahead and unwrap.
            .map(|&widget_id| {
                ac.fetch_widget::<Widget>(widget_id.0)
                    .expect("Error loading widget!")
            })
            .collect();

        let save_tabs = |cfg_tabs: &mut Vec<String>, nb: &Notebook| {
            cfg_tabs.clear();
            for child in nb.get_children() {
                for (idx, tab) in tabs.iter().enumerate() {
                    if child == *tab {
                        cfg_tabs.push(TABS[idx].0.to_owned());
                    }
                }
            }
        };

        save_tabs(&mut config.left_tabs, &lnb);
        save_tabs(&mut config.right_tabs, &rnb);

        config.left_page_selected = lnb.get_property_page();
        config.right_page_selected = rnb.get_property_page();
    }

    gtk::main_quit();
    Inhibit(false)
}

fn layout_tabs_window(win: &Window, ac: &AppContext) -> Result<(), SondeError> {
    let cfg = ac.config.borrow();

    let pane: Paned = ac.fetch_widget("main_pane_view")?;

    let (width, height, pane_position) =
        { (cfg.window_width, cfg.window_height, cfg.pane_position) };

    if width > 0 || height > 0 {
        win.resize(width, height);
    }

    if pane_position > 0.0 {
        let (width, _) = win.get_size();
        let pos = (width as f32 * pane_position).round() as i32;

        debug_assert!(pos < width);
        pane.set_position(pos);
    }

    if !(cfg.left_tabs.is_empty() && cfg.right_tabs.is_empty()) {
        if let (Ok(lnb), Ok(rnb)) = (
            ac.fetch_widget::<Notebook>("left_notebook"),
            ac.fetch_widget::<Notebook>("right_notebook"),
        ) {
            let restore_tabs = |cfg_tabs: &Vec<String>, tgt_nb: &Notebook, other_nb: &Notebook| {
                for tab_id in cfg_tabs {
                    TABS.iter().position(|&s| s.0 == tab_id).and_then(|idx| {
                        ac.fetch_widget::<Widget>(TABS[idx].0)
                            .ok()
                            .and_then(|widget| {
                                let tgt_children = tgt_nb.get_children();
                                let other_children = other_nb.get_children();

                                if tgt_children.contains(&widget) {
                                    tgt_nb.remove(&widget);
                                } else if other_children.contains(&widget) {
                                    other_nb.remove(&widget);
                                }

                                tgt_nb.add(&widget);
                                tgt_nb.set_tab_label_text(&widget, TABS[idx].1);
                                tgt_nb.set_tab_detachable(&widget, true);
                                tgt_nb.set_tab_reorderable(&widget, true);

                                Some(())
                            })
                    });
                }
            };

            restore_tabs(&cfg.left_tabs, &lnb, &rnb);
            restore_tabs(&cfg.right_tabs, &rnb, &lnb);

            lnb.set_property_page(cfg.left_page_selected);
            rnb.set_property_page(cfg.right_page_selected);
        }
    }

    Ok(())
}
<commit_msg>cargo fmt [ci skip]<commit_after>use crate::{
    app::{AppContext, AppContextPointer},
    errors::SondeError,
};
use gdk::Event;
use gtk::{
    self, prelude::*, Button, HeaderBar, IconSize, Notebook, Orientation, Paned, Separator, Widget,
    Window,
};
use std::rc::Rc;

mod menu_callbacks;

const TABS: [(&str, &str); 8] = [
    ("skew_t", "Skew T"),
    ("hodograph_area", "Hodograph"),
    ("fire_plume_container", "Fire Plume"),
    ("text_area_container", "Text"),
    ("control_area", "Controls"),
    ("profiles_area_container", "Profiles"),
    ("indexes_scrolled_window", "Indexes"),
    ("provider_data_text_container", "Provider Data"),
];

pub fn set_up_main_window(ac: &AppContextPointer) -> Result<(), SondeError> {
    connect_header_bar(ac)?;
    configure_main_window(ac)?;

    Ok(())
}

fn connect_header_bar(ac: &AppContextPointer) -> Result<(), SondeError> {
    let win: Window = ac.fetch_widget("main_window")?;
    let header_bar: HeaderBar = ac.fetch_widget("header-bar")?;

    let open_button = Button::new_from_icon_name(Some("document-open"), IconSize::SmallToolbar);
    open_button.set_label("Open");
    open_button.set_always_show_image(true);
    let ac1 = Rc::clone(ac);
    let win1 = win.clone();
    open_button.connect_clicked(move |_| {
        menu_callbacks::open_toolbar_callback(&ac1, &win1);
    });
    header_bar.pack_start(&open_button);

    let save_image_button =
        Button::new_from_icon_name(Some("insert-image"), IconSize::SmallToolbar);
    save_image_button.set_label("Save Image");
    save_image_button.set_always_show_image(true);
    let ac1 = Rc::clone(ac);
    let win1 = win.clone();
    save_image_button.connect_clicked(move |_| menu_callbacks::save_image_callback(&ac1, &win1));
    header_bar.pack_start(&save_image_button);

    header_bar.pack_start(&Separator::new(Orientation::Vertical));

    let first_button = Button::new_from_icon_name(Some("go-first"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    first_button.connect_clicked(move |_| ac1.display_first());
    header_bar.pack_start(&first_button);

    let previous_button =
        Button::new_from_icon_name(Some("media-skip-backward"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    previous_button.connect_clicked(move |_| ac1.display_previous());
    header_bar.pack_start(&previous_button);

    let next_button =
        Button::new_from_icon_name(Some("media-skip-forward"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    next_button.connect_clicked(move |_| ac1.display_next());
    header_bar.pack_start(&next_button);

    let last_button = Button::new_from_icon_name(Some("go-last"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    last_button.connect_clicked(move |_| ac1.display_last());
    header_bar.pack_start(&last_button);

    header_bar.pack_start(&Separator::new(Orientation::Vertical));

    let zoom_in_button = Button::new_from_icon_name(Some("zoom-in"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    zoom_in_button.connect_clicked(move |_| ac1.zoom_in());
    header_bar.pack_start(&zoom_in_button);

    let zoom_out_button = Button::new_from_icon_name(Some("zoom-out"), IconSize::SmallToolbar);
    let ac1 = Rc::clone(ac);
    zoom_out_button.connect_clicked(move |_| ac1.zoom_out());
    header_bar.pack_start(&zoom_out_button);

    let quit_button =
        Button::new_from_icon_name(Some("application-exit-symbolic"), IconSize::SmallToolbar);
    quit_button.connect_clicked(|_| {
        gtk::main_quit();
    });
    header_bar.pack_end(&quit_button);

    Ok(())
}

fn configure_main_window(ac: &AppContextPointer) -> Result<(), SondeError> {
    let window: Window = ac.fetch_widget("main_window")?;

    layout_tabs_window(&window, ac)?;

    let ac1 = Rc::clone(ac);
    window.connect_delete_event(move |win, ev| on_delete(win, ev, &ac1));

    window.show_all();

    Ok(())
}

fn on_delete(win: &Window, _ev: &Event, ac: &AppContext) -> Inhibit {
    let mut config = ac.config.borrow_mut();

    // Save the window dimensions
    let (width, height) = win.get_size();
    config.window_width = width;
    config.window_height = height;

    // Save the Paned view slider position.
    if let Ok(pane) = ac.fetch_widget::<Paned>("main_pane_view") {
        let pos = (pane.get_position() as f32) / (width as f32);
        config.pane_position = pos;
    }

    // Save the tabs, which notebook they are in, their order, and which ones were selected.
    if let (Ok(lnb), Ok(rnb)) = (
        ac.fetch_widget::<Notebook>("left_notebook"),
        ac.fetch_widget::<Notebook>("right_notebook"),
    ) {
        let tabs: Vec<Widget> = TABS
            .iter()
            // If there is an error here, it will ALWAYS fail. So go ahead and unwrap.
            .map(|&widget_id| {
                ac.fetch_widget::<Widget>(widget_id.0)
                    .expect("Error loading widget!")
            })
            .collect();

        let save_tabs = |cfg_tabs: &mut Vec<String>, nb: &Notebook| {
            cfg_tabs.clear();
            for child in nb.get_children() {
                for (idx, tab) in tabs.iter().enumerate() {
                    if child == *tab {
                        cfg_tabs.push(TABS[idx].0.to_owned());
                    }
                }
            }
        };

        save_tabs(&mut config.left_tabs, &lnb);
        save_tabs(&mut config.right_tabs, &rnb);

        config.left_page_selected = lnb.get_property_page();
        config.right_page_selected = rnb.get_property_page();
    }

    gtk::main_quit();
    Inhibit(false)
}

fn layout_tabs_window(win: &Window, ac: &AppContext) -> Result<(), SondeError> {
    let cfg = ac.config.borrow();

    let pane: Paned = ac.fetch_widget("main_pane_view")?;

    let (width, height, pane_position) =
        { (cfg.window_width, cfg.window_height, cfg.pane_position) };

    if width > 0 || height > 0 {
        win.resize(width, height);
    }

    if pane_position > 0.0 {
        let (width, _) = win.get_size();
        let pos = (width as f32 * pane_position).round() as i32;

        debug_assert!(pos < width);
        pane.set_position(pos);
    }

    if !(cfg.left_tabs.is_empty() && cfg.right_tabs.is_empty()) {
        if let (Ok(lnb), Ok(rnb)) = (
            ac.fetch_widget::<Notebook>("left_notebook"),
            ac.fetch_widget::<Notebook>("right_notebook"),
        ) {
            let restore_tabs = |cfg_tabs: &Vec<String>, tgt_nb: &Notebook, other_nb: &Notebook| {
                for tab_id in cfg_tabs {
                    TABS.iter().position(|&s| s.0 == tab_id).and_then(|idx| {
                        ac.fetch_widget::<Widget>(TABS[idx].0)
                            .ok()
                            .and_then(|widget| {
                                let tgt_children = tgt_nb.get_children();
                                let other_children = other_nb.get_children();

                                if tgt_children.contains(&widget) {
                                    tgt_nb.remove(&widget);
                                } else if other_children.contains(&widget) {
                                    other_nb.remove(&widget);
                                }

                                tgt_nb.add(&widget);
                                tgt_nb.set_tab_label_text(&widget, TABS[idx].1);
                                tgt_nb.set_tab_detachable(&widget, true);
                                tgt_nb.set_tab_reorderable(&widget, true);

                                Some(())
                            })
                    });
                }
            };

            restore_tabs(&cfg.left_tabs, &lnb, &rnb);
            restore_tabs(&cfg.right_tabs, &rnb, &lnb);

            lnb.set_property_page(cfg.left_page_selected);
            rnb.set_property_page(cfg.right_page_selected);
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::result::Result;
use std::marker::PhantomData;
use std::error::Error;
use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;

///
/// Represents a tool made from a function
///
pub struct FnTool<TIn: Deserialize, TOut: Serialize, TErr: Serialize> {
    function: Box<Fn(TIn, &Environment) -> Result<TOut, TErr> + Send + Sync>
}

///
/// Creates a Tool from a function that can produce an error and uses an environment
///
pub fn make_dynamic_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn, &Environment) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    FnTool { function: Box::new(function) }
}

///
/// Creates a Tool from a function that can produce an error but does not use an environment
///
pub fn make_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    make_dynamic_tool(move |input, _| function(input))
}

///
/// Creates a Tool from a function that cannot produce an error and doesn't use an environment
///
pub fn make_pure_tool<TIn: Deserialize, TOut: Serialize, F: 'static+Send+Sync+Fn(TIn) -> TOut>(function: F) -> FnTool<TIn, TOut, ()> {
    make_dynamic_tool(move |input, _| Ok(function(input)))
}

impl<TIn, TOut, TErr> Tool for FnTool<TIn, TOut, TErr>
where TIn: Deserialize, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = (self.function)(input_decoded, environment);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}

///
/// Represents a tool with Rust types
///
pub struct TypedTool<TIn: Serialize, TOut: Deserialize> {
    param1: PhantomData<TIn>,
    param2: PhantomData<TOut>,
    tool: Box<Tool>

    // tool could be &'a Tool and it would work as Box<Tool> implements Tool - except rust's deref behaviour
    // gets in the way: trying to pass a reference to a box actually passes a reference to the boxed thing, which
    // then doesn't work because the size is unknown
}

impl<TIn: Serialize, TOut: Deserialize> TypedTool<TIn, TOut> {
    ///
    /// Creates an object that can be used to invoke a tool with typed parameters instead of pure JSON
    ///
    pub fn from(tool: Box<Tool>) -> TypedTool<TIn, TOut> {
        TypedTool { param1: PhantomData, param2: PhantomData, tool: tool }
    }

    ///
    /// Invokes this tool
    ///
    pub fn invoke(&self, input: TIn, environment: &Environment) -> Result<TOut, Value> {
        let json_input = to_value(input);

        match json_input {
            Ok(json_input) => {
                let json_output = self.tool.invoke_json(json_input, environment);

                match json_output {
                    Ok(json_output) => {
                        let result = from_value::<TOut>(json_output);
                        match result {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Result decode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(json_error) => {
                        Err(json_error)
                    }
                }
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Input encode failed",
                    "description":  erm.description()
                }])
            }
        }
    }
}

///
/// Extension trait for environments that makes it possible to get a Rust typed tool immediately
///
pub trait TypedEnvironment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>;
}

impl<TEnv> TypedEnvironment for TEnv where TEnv: Environment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<'a, TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError> {
        self.get_json_tool(name).map(|tool| TypedTool::from(tool))
    }
}

#[cfg(test)]
mod test {
    use super::super::super::*;
    use super::super::*;
    use std::result::Result;
    use serde_json::*;              // Rust says unused, but the json! macro will not work without this

    #[derive(Serialize, Deserialize)]
    struct TestIn {
        input: i32
    }

    #[derive(Serialize, Deserialize, PartialEq, Eq)]
    struct TestOut {
        output: i32
    }

    fn test_tool(x: TestIn) -> Result<TestOut, ()> {
        Ok(TestOut { output: x.input + 1 })
    }

    #[test]
    fn can_call_tool_via_json_interface() {
        let tool        = make_tool(test_tool);
        let environment = EmptyEnvironment::new();
        let result      = tool.invoke_json(json![{ "input": 4 }], &environment);

        assert!(result == Ok(json![{ "output": 5 }]));
    }

    #[test]
    fn can_call_tool_via_typed_interface() {
        let tool        = make_pure_tool(|x: i32| { x+1 });
        let environment = EmptyEnvironment::new();

        let typed_tool  = TypedTool::from(Box::new(tool));
        let result      = typed_tool.invoke(4, &environment);

        assert!(result == Ok(5));
    }
}
<commit_msg>‘a not needed here<commit_after>use std::result::Result;
use std::marker::PhantomData;
use std::error::Error;
use serde::*;
use serde_json::*;

use super::super::tool::*;
use super::super::environment::*;

///
/// Represents a tool made from a function
///
pub struct FnTool<TIn: Deserialize, TOut: Serialize, TErr: Serialize> {
    function: Box<Fn(TIn, &Environment) -> Result<TOut, TErr> + Send + Sync>
}

///
/// Creates a Tool from a function that can produce an error and uses an environment
///
pub fn make_dynamic_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn, &Environment) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    FnTool { function: Box::new(function) }
}

///
/// Creates a Tool from a function that can produce an error but does not use an environment
///
pub fn make_tool<TIn: Deserialize, TOut: Serialize, TErr: Serialize, F: 'static+Send+Sync+Fn(TIn) -> Result<TOut, TErr>>(function: F) -> FnTool<TIn, TOut, TErr> {
    make_dynamic_tool(move |input, _| function(input))
}

///
/// Creates a Tool from a function that cannot produce an error and doesn't use an environment
///
pub fn make_pure_tool<TIn: Deserialize, TOut: Serialize, F: 'static+Send+Sync+Fn(TIn) -> TOut>(function: F) -> FnTool<TIn, TOut, ()> {
    make_dynamic_tool(move |input, _| Ok(function(input)))
}

impl<TIn, TOut, TErr> Tool for FnTool<TIn, TOut, TErr>
where TIn: Deserialize, TOut: Serialize, TErr: Serialize {
    fn invoke_json(&self, input: Value, environment: &Environment) -> Result<Value, Value> {
        // Decode
        let input_decoded = from_value::<TIn>(input);

        // Chain into the tool itself
        match input_decoded {
            Ok(input_decoded) => {
                // Successfully decoded as the tool's input format
                let result = (self.function)(input_decoded, environment);

                // Encode the error or the result
                // The encoding can go wrong, and we need to handle it slightly differently for the error or the success case
                match result {
                    Ok(res) => {
                        let encoded = to_value(res);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "JSON encode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(erm) => {
                        let encoded = to_value(erm);
                        match encoded {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Error encode failed",
                                "description":  erm.description()
                            }])
                        }
                    }
                }
            },

            Err(input_error) => {
                // Input does not match the format expected by the tool
                Err(json![{
                    "error":        "JSON input decode failed",
                    "description":  input_error.description(),
                }])
            }
        }
    }
}

///
/// Represents a tool with Rust types
///
pub struct TypedTool<TIn: Serialize, TOut: Deserialize> {
    param1: PhantomData<TIn>,
    param2: PhantomData<TOut>,
    tool: Box<Tool>

    // tool could be &'a Tool and it would work as Box<Tool> implements Tool - except rust's deref behaviour
    // gets in the way: trying to pass a reference to a box actually passes a reference to the boxed thing, which
    // then doesn't work because the size is unknown
}

impl<TIn: Serialize, TOut: Deserialize> TypedTool<TIn, TOut> {
    ///
    /// Creates an object that can be used to invoke a tool with typed parameters instead of pure JSON
    ///
    pub fn from(tool: Box<Tool>) -> TypedTool<TIn, TOut> {
        TypedTool { param1: PhantomData, param2: PhantomData, tool: tool }
    }

    ///
    /// Invokes this tool
    ///
    pub fn invoke(&self, input: TIn, environment: &Environment) -> Result<TOut, Value> {
        let json_input = to_value(input);

        match json_input {
            Ok(json_input) => {
                let json_output = self.tool.invoke_json(json_input, environment);

                match json_output {
                    Ok(json_output) => {
                        let result = from_value::<TOut>(json_output);
                        match result {
                            Ok(final_value) => Ok(final_value),
                            Err(erm)        => Err(json![{
                                "error":        "Result decode failed",
                                "description":  erm.description()
                            }])
                        }
                    },

                    Err(json_error) => {
                        Err(json_error)
                    }
                }
            },

            Err(erm) => {
                Err(json![{
                    "error":        "Input encode failed",
                    "description":  erm.description()
                }])
            }
        }
    }
}

///
/// Extension trait for environments that makes it possible to get a Rust typed tool immediately
///
pub trait TypedEnvironment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError>;
}

impl<TEnv> TypedEnvironment for TEnv where TEnv: Environment {
    ///
    /// Retrieves a tool using a Rust interface by name
    ///
    fn get_typed_tool<TIn: Serialize, TOut: Deserialize>(&self, name: &str) -> Result<TypedTool<TIn, TOut>, RetrieveToolError> {
        self.get_json_tool(name).map(|tool| TypedTool::from(tool))
    }
}

#[cfg(test)]
mod test {
    use super::super::super::*;
    use super::super::*;
    use std::result::Result;
    use serde_json::*;              // Rust says unused, but the json! macro will not work without this

    #[derive(Serialize, Deserialize)]
    struct TestIn {
        input: i32
    }

    #[derive(Serialize, Deserialize, PartialEq, Eq)]
    struct TestOut {
        output: i32
    }

    fn test_tool(x: TestIn) -> Result<TestOut, ()> {
        Ok(TestOut { output: x.input + 1 })
    }

    #[test]
    fn can_call_tool_via_json_interface() {
        let tool        = make_tool(test_tool);
        let environment = EmptyEnvironment::new();
        let result      = tool.invoke_json(json![{ "input": 4 }], &environment);

        assert!(result == Ok(json![{ "output": 5 }]));
    }

    #[test]
    fn can_call_tool_via_typed_interface() {
        let tool        = make_pure_tool(|x: i32| { x+1 });
        let environment = EmptyEnvironment::new();

        let typed_tool  = TypedTool::from(Box::new(tool));
        let result      = typed_tool.invoke(4, &environment);

        assert!(result == Ok(5));
    }
}
<|endoftext|>


<commit_before>use datum::*;
use primitive::*;
use numeric::LNumeric;
use core::hashmap::linear::LinearMap;

struct Runtime {
    stdin: @io::Reader,
    stdout: @io::Writer,
    stderr: @io::Writer,
    global: LinearMap<~str, @LDatum>
}

#[deriving(Eq)]
enum RuntimeError {
    UnknownLocalVariable(~str),
    NotImplemented,
    NotCallable,
    NotList,
    ArgNumError,
    TypeError,
    DivideByZeroError,
}

impl ToStr for RuntimeError {
    fn to_str(&self) -> ~str {
        err_to_str(self)
    }
}

priv fn err_to_str(&err: &RuntimeError) -> ~str {
    match err {
        UnknownLocalVariable(name) => ~"unknown local variable: " + copy name,
        NotImplemented => ~"not implemented",
        NotCallable => ~"not callable",
        NotList => ~"not list",
        ArgNumError => ~"bad number of arguments",
        TypeError => ~"type error",
        DivideByZeroError => ~"divide by zero",
    }
}

fn load_prelude() -> LinearMap<~str, @LDatum> {
    let mut map = LinearMap::new();
    for vec::each(prelude()) |&pair| {
        let (key, func) = pair;
        map.insert(key, @LPrim(func));
    }
    map
}

priv fn call_prim1(args: &[@LDatum],
                op: &fn(@LDatum) -> Result<@LDatum, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    if args.len() == 1 {
        op(args[0])
    } else {
        Err(ArgNumError)
    }
}

priv fn call_num_prim2(args: &[@LDatum],
                    op: &fn(&LNumeric, &LNumeric) -> Result<@LDatum, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    if args.len() == 2 {
        match *args[0] {
            LNum(lhs) => match *args[1] {
                LNum(rhs) => op(&lhs, &rhs),
                _ => Err(TypeError),
            },
            _ => Err(TypeError),
        }
    } else {
        Err(ArgNumError)
    }
}

pub impl Runtime {
    fn new_std() -> Runtime {
        Runtime {
            stdin: io::stdin(),
            stdout: io::stdout(),
            stderr: io::stderr(),
            global: load_prelude(),
        }
    }

    fn eval(&mut self, val: @LDatum) -> Result<@LDatum, RuntimeError> {
        match copy *val {
            LIdent(name) =>
                match self.global.find(&name) {
                    Some(&datum) => Ok(datum),
                    None => Err(UnknownLocalVariable(name)),
                },
            LCons(fexpr, aexpr) =>
                match aexpr.to_list() {
                    None => Err(NotList),
                    Some(aexprs) => {
                        match self.eval(fexpr) {
                            Ok(@LPrim(f)) => {
                                match result::map_vec(aexprs, |&expr| self.eval(expr)) {
                                    Ok(args) => self.call_prim(f, args),
                                    Err(e) => Err(e),
                                }
                            }
                            Ok(_) => Err(NotCallable),
                            Err(e) => Err(e),
                        }
                    },
                },
            LNil => Ok(@LNil),
            _ => Ok(val),
        }
    }

    fn call_prim(&mut self, f: PFunc, args: &[@LDatum]) -> Result<@LDatum, RuntimeError> {
        match f {
            PEval => do call_prim1(args) |arg| {
                self.eval(arg)
            },
            PAdd => do call_num_prim2(args) |&lhs, &rhs| {
                Ok(@LNum(lhs + rhs))
            },
            PSub => do call_num_prim2(args) |&lhs, &rhs| {
                Ok(@LNum(lhs - rhs))
            },
            PMul => do call_num_prim2(args) |&lhs, &rhs| {
                Ok(@LNum(lhs * rhs))
            },
            PDiv => do call_num_prim2(args) |&lhs, &rhs| {
                if rhs.is_zero() {
                    Err(DivideByZeroError)
                } else {
                    Ok(@LNum(lhs / rhs))
                }
            },
        }
    }
}
<commit_msg>rename UnknownLocalVariable to UnboundVariable<commit_after>use datum::*;
use primitive::*;
use numeric::LNumeric;
use core::hashmap::linear::LinearMap;

struct Runtime {
    stdin: @io::Reader,
    stdout: @io::Writer,
    stderr: @io::Writer,
    global: LinearMap<~str, @LDatum>
}

#[deriving(Eq)]
enum RuntimeError {
    UnboundVariable(~str),
    NotImplemented,
    NotCallable,
    NotList,
    ArgNumError,
    TypeError,
    DivideByZeroError,
}

impl ToStr for RuntimeError {
    fn to_str(&self) -> ~str {
        err_to_str(self)
    }
}

priv fn err_to_str(&err: &RuntimeError) -> ~str {
    match err {
        UnboundVariable(name) => ~"unbound variable: " + copy name,
        NotImplemented => ~"not implemented",
        NotCallable => ~"not callable",
        NotList => ~"not list",
        ArgNumError => ~"bad number of arguments",
        TypeError => ~"type error",
        DivideByZeroError => ~"divide by zero",
    }
}

fn load_prelude() -> LinearMap<~str, @LDatum> {
    let mut map = LinearMap::new();
    for vec::each(prelude()) |&pair| {
        let (key, func) = pair;
        map.insert(key, @LPrim(func));
    }
    map
}

priv fn call_prim1(args: &[@LDatum],
                op: &fn(@LDatum) -> Result<@LDatum, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    if args.len() == 1 {
        op(args[0])
    } else {
        Err(ArgNumError)
    }
}

priv fn call_num_prim2(args: &[@LDatum],
                    op: &fn(&LNumeric, &LNumeric) -> Result<@LDatum, RuntimeError>)
    -> Result<@LDatum, RuntimeError>
{
    if args.len() == 2 {
        match *args[0] {
            LNum(lhs) => match *args[1] {
                LNum(rhs) => op(&lhs, &rhs),
                _ => Err(TypeError),
            },
            _ => Err(TypeError),
        }
    } else {
        Err(ArgNumError)
    }
}

pub impl Runtime {
    fn new_std() -> Runtime {
        Runtime {
            stdin: io::stdin(),
            stdout: io::stdout(),
            stderr: io::stderr(),
            global: load_prelude(),
        }
    }

    fn eval(&mut self, val: @LDatum) -> Result<@LDatum, RuntimeError> {
        match copy *val {
            LIdent(name) =>
                match self.global.find(&name) {
                    Some(&datum) => Ok(datum),
                    None => Err(UnboundVariable(name)),
                },
            LCons(fexpr, aexpr) =>
                match aexpr.to_list() {
                    None => Err(NotList),
                    Some(aexprs) => {
                        match self.eval(fexpr) {
                            Ok(@LPrim(f)) => {
                                match result::map_vec(aexprs, |&expr| self.eval(expr)) {
                                    Ok(args) => self.call_prim(f, args),
                                    Err(e) => Err(e),
                                }
                            }
                            Ok(_) => Err(NotCallable),
                            Err(e) => Err(e),
                        }
                    },
                },
            LNil => Ok(@LNil),
            _ => Ok(val),
        }
    }

    fn call_prim(&mut self, f: PFunc, args: &[@LDatum]) -> Result<@LDatum, RuntimeError> {
        match f {
            PEval => do call_prim1(args) |arg| {
                self.eval(arg)
            },
            PAdd => do call_num_prim2(args) |&lhs, &rhs| {
                Ok(@LNum(lhs + rhs))
            },
            PSub => do call_num_prim2(args) |&lhs, &rhs| {
                Ok(@LNum(lhs - rhs))
            },
            PMul => do call_num_prim2(args) |&lhs, &rhs| {
                Ok(@LNum(lhs * rhs))
            },
            PDiv => do call_num_prim2(args) |&lhs, &rhs| {
                if rhs.is_zero() {
                    Err(DivideByZeroError)
                } else {
                    Ok(@LNum(lhs / rhs))
                }
            },
        }
    }
}
<|endoftext|>


<commit_before>use super::lsm::service::*;
use super::trees::*;
use crate::ram::types::Id;
use crate::ram::types::RandValue;
use bifrost::conshash::ConsistentHashing;
use bifrost::raft::state_machine::StateMachineCtl;
use bifrost::raft::RaftService;
use bifrost::rpc::{RPCClient, RPCError, DEFAULT_CLIENT_POOL};
use bifrost::utils;
use futures::prelude::*;
use std::collections::BTreeMap;
use std::sync::Arc;

pub struct MasterTreeSM {
    tree: BTreeMap<EntryKey, Id>,
    raft_svr: Arc<RaftService>,
    conshash: Arc<ConsistentHashing>,
    sm_id: u64,
}

raft_state_machine! {
    def qry locate_key(entry: EntryKey) -> (EntryKey, Id, EntryKey);
    def cmd split(new_tree: Id, pivot: EntryKey);
    // No subscription for clients
}

impl StateMachineCmds for MasterTreeSM {
    fn locate_key(&self, entry: EntryKey) -> BoxFuture<(EntryKey, Id, EntryKey)> {
        let (lower, id) = self
            .tree
            .range(..entry.clone())
            .last()
            .map(|(key, id)| (key.clone(), *id))
            .unwrap();
        let upper = self
            .tree
            .range(entry..)
            .next()
            .map(|(key, _)| key.clone())
            .unwrap();
        future::ready((lower, id, upper)).boxed()
    }

    fn split(&mut self, new_tree: Id, pivot: EntryKey) -> BoxFuture<()> {
        // Call this after the tree have been split and persisted
        let upper_bound = self.tree.range(pivot.clone()..).next().unwrap().0.clone();
        self.tree.insert(pivot.clone(), new_tree);
        self.load_sub_tree(new_tree, pivot, upper_bound).boxed()
    }
}

impl StateMachineCtl for MasterTreeSM {
    raft_sm_complete!();
    fn id(&self) -> u64 {
        self.sm_id
    }
    fn snapshot(&self) -> Option<Vec<u8>> {
        Some(utils::serde::serialize(&self.tree))
    }
    fn recover(&mut self, data: Vec<u8>) -> BoxFuture<()> {
        let tree = utils::serde::deserialize(&data).unwrap();
        self.tree = tree;
        future::ready(()).boxed()
    }
}

impl MasterTreeSM {
    pub async fn new(
        raft_svr: &Arc<RaftService>,
        conshash: &Arc<ConsistentHashing>,
        sm_id: u64,
    ) -> Self {
        let mut tree = BTreeMap::new();
        let genesis_id = Id::rand();
        tree.insert(min_entry_key(), genesis_id);
        locate_tree_server_from_conshash(&genesis_id, conshash)
            .await
            .unwrap()
            .crate_tree(genesis_id, Boundary::new(min_entry_key(), max_entry_key()))
            .await
            .unwrap();
        Self {
            tree,
            raft_svr: raft_svr.clone(),
            conshash: conshash.clone(),
            sm_id,
        }
    }
    async fn load_sub_tree(&self, id: Id, lower: EntryKey, upper: EntryKey) {
        if self.raft_svr.is_leader() {
            // Only the leader can initiate the request to load the sub tree
            let client = self.locate_tree_server(&id).await.unwrap();
            client
                .load_tree(id, Boundary::new(lower, upper))
                .await
                .unwrap()
        }
    }

    async fn locate_tree_server(&self, id: &Id) -> Result<Arc<LSMServiceClient>, RPCError> {
        locate_tree_server_from_conshash(id, &self.conshash).await
    }
}
<commit_msg>fix compile<commit_after>use super::lsm::service::AsyncServiceClient as LSMServiceClient;
use super::lsm::service::*;
use super::trees::*;
use crate::ram::types::Id;
use crate::ram::types::RandValue;
use bifrost::conshash::ConsistentHashing;
use bifrost::raft::state_machine::StateMachineCtl;
use bifrost::raft::RaftService;
use bifrost::rpc::{RPCClient, RPCError, DEFAULT_CLIENT_POOL};
use bifrost::utils;
use futures::prelude::*;
use std::collections::BTreeMap;
use std::sync::Arc;

pub struct MasterTreeSM {
    tree: BTreeMap<EntryKey, Id>,
    raft_svr: Arc<RaftService>,
    conshash: Arc<ConsistentHashing>,
    sm_id: u64,
}

raft_state_machine! {
    def qry locate_key(entry: EntryKey) -> (EntryKey, Id, EntryKey);
    def cmd split(new_tree: Id, pivot: EntryKey);
    // No subscription for clients
}

impl StateMachineCmds for MasterTreeSM {
    fn locate_key(&self, entry: EntryKey) -> BoxFuture<(EntryKey, Id, EntryKey)> {
        let (lower, id) = self
            .tree
            .range(..entry.clone())
            .last()
            .map(|(key, id)| (key.clone(), *id))
            .unwrap();
        let upper = self
            .tree
            .range(entry..)
            .next()
            .map(|(key, _)| key.clone())
            .unwrap();
        future::ready((lower, id, upper)).boxed()
    }

    fn split(&mut self, new_tree: Id, pivot: EntryKey) -> BoxFuture<()> {
        // Call this after the tree have been split and persisted
        let upper_bound = self.tree.range(pivot.clone()..).next().unwrap().0.clone();
        self.tree.insert(pivot.clone(), new_tree);
        self.load_sub_tree(new_tree, pivot, upper_bound).boxed()
    }
}

impl StateMachineCtl for MasterTreeSM {
    raft_sm_complete!();
    fn id(&self) -> u64 {
        self.sm_id
    }
    fn snapshot(&self) -> Option<Vec<u8>> {
        Some(utils::serde::serialize(&self.tree))
    }
    fn recover(&mut self, data: Vec<u8>) -> BoxFuture<()> {
        let tree = utils::serde::deserialize(&data).unwrap();
        self.tree = tree;
        future::ready(()).boxed()
    }
}

impl MasterTreeSM {
    pub async fn new(
        raft_svr: &Arc<RaftService>,
        conshash: &Arc<ConsistentHashing>,
        sm_id: u64,
    ) -> Self {
        let mut tree = BTreeMap::new();
        let genesis_id = Id::rand();
        tree.insert(min_entry_key(), genesis_id);
        locate_tree_server_from_conshash(&genesis_id, conshash)
            .await
            .unwrap()
            .crate_tree(genesis_id, Boundary::new(min_entry_key(), max_entry_key()))
            .await
            .unwrap();
        Self {
            tree,
            raft_svr: raft_svr.clone(),
            conshash: conshash.clone(),
            sm_id,
        }
    }
    async fn load_sub_tree(&self, id: Id, lower: EntryKey, upper: EntryKey) {
        if self.raft_svr.is_leader() {
            // Only the leader can initiate the request to load the sub tree
            let client = self.locate_tree_server(&id).await.unwrap();
            client
                .load_tree(id, Boundary::new(lower, upper))
                .await
                .unwrap()
        }
    }

    async fn locate_tree_server(&self, id: &Id) -> Result<Arc<LSMServiceClient>, RPCError> {
        locate_tree_server_from_conshash(id, &self.conshash).await
    }
}
<|endoftext|>


<commit_before>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/main.rs
// - Kernel main
#![crate_name="kernel"]
#![crate_type="lib"]
#![feature(no_std)]
#![feature(asm)]	// Enables the asm! syntax extension
#![feature(box_syntax)]	// Enables 'box' syntax
#![feature(thread_local)]	// Allows use of thread_local
#![feature(lang_items)]	// Allow definition of lang_items
#![feature(core)]	// silences warnings about write!
#![feature(optin_builtin_traits)]	// Negative impls
#![feature(unique)]	// Unique
#![feature(slice_patterns)]	// Slice (array) destructuring patterns, used by multiboot code
#![feature(step_by)]	// Range::step_by
#![feature(linkage)]	// allows using #[linkage="external"]
#![feature(const_fn)]	// Allows defining `const fn`
#![no_std]

#![feature(plugin)]
#![feature(custom_attribute)]
#![plugin(tag_safe)]
use prelude::*;

#[macro_use]
extern crate core;

pub use arch::memory::PAGE_SIZE;

#[doc(hidden)]
#[macro_use] pub mod logmacros;
#[doc(hidden)]
#[macro_use] pub mod macros;
#[doc(hidden)]
#[macro_use] #[cfg(arch__amd64)] #[path="arch/amd64/mod-macros.rs"] pub mod arch_macros;

// Evil Hack: For some reason, write! (and friends) will expand pointing to std instead of core
#[doc(hidden)]
mod std {
	pub use core::option;
	pub use core::{default,fmt,cmp};
	pub use core::marker;	// needed for derive(Copy)
	pub use core::iter;	// needed for 'for'
}

/// Kernel's version of 'std::prelude'
pub mod prelude;

/// Library datatypes (Vec, Queue, ...)
#[macro_use]
pub mod lib;	// Clone of libstd

/// Heavy synchronisation primitives (Mutex, Semaphore, RWLock, ...)
#[macro_use]
pub mod sync;

/// Asynchrnous wait support
pub mod async;

/// Logging framework
pub mod logging;
/// Memory management (physical, virtual, heap)
pub mod memory;
/// Thread management
#[macro_use]
pub mod threads;
/// Timekeeping (timers and wall time)
pub mod time;

// Module/Executable loading (and symbol lookup)
pub mod loading;
/// Module management (loading and initialisation of kernel modules)
pub mod modules;

/// Meta devices (the Hardware Abstraction Layer)
pub mod metadevs;
/// Device to driver mapping manager
///
/// Starts driver instances for the devices it sees
pub mod device_manager;

/// User output, via a kernel-provided compositing "WM"
pub mod gui;

// Public for driver modules
pub mod vfs;

mod config;

/// Stack unwinding (panic) handling
pub mod unwind;

pub mod irqs;

/// Built-in device drivers
mod hw;

/// Achitecture-specific code - AMD64 (aka x86-64)
#[macro_use]
#[cfg(arch__amd64)] #[path="arch/amd64/mod.rs"] pub mod arch;	// Needs to be pub for exports to be avaliable

/// Kernel entrypoint
#[no_mangle]
pub extern "C" fn kmain()
{
	log_notice!("Tifflin Kernel v{} build {} starting", env!("TK_VERSION"), env!("TK_BUILD"));
	log_notice!("> Git state : {}", env!("TK_GITSPEC"));
	log_notice!("> Built with {}", env!("RUST_VERSION"));

	// Initialise core services before attempting modules
	::memory::phys::init();
	::memory::virt::init();
	::memory::heap::init();
	::threads::init();

	log_log!("Command line = '{}'", ::arch::boot::get_boot_string());
	// TODO: Parse command line into config

	// Dump active video mode
	let vidmode = ::arch::boot::get_video_mode();
	match vidmode {
	Some(m) => {
		log_debug!("Video mode : {}x{} @ {:#x}", m.width, m.height, m.base);
		::metadevs::video::set_boot_mode(m);
		},
	None => log_debug!("No video mode present")
	}

	// Modules (dependency tree included)
	// - Requests that the GUI be started as soon as possible
	::modules::init(&["GUI"]);

	// Yield to allow init threads to run
	::threads::yield_time();

	// Run system init
	sysinit();

	// Thread 0 idle loop
	log_info!("Entering idle");
	loop
	{
		log_trace!("TID0 napping");
		::threads::yield_time();
	}
}

// Initialise the system once drivers are up
fn sysinit()
{
	use metadevs::storage::VolumeHandle;
	use vfs::{mount,handle};
	use vfs::Path;

	// 1. Mount /system to the specified volume
	let sysdisk = ::config::get_string(::config::Value::SysDisk);
	match VolumeHandle::open_named(sysdisk)
	{
	Err(e) => {
		log_error!("Unable to open /system volume {}: {}", sysdisk, e);
		return ;
		},
	Ok(vh) => match mount::mount("/system".as_ref(), vh, "", &[])
		{
		Ok(_) => {},
		Err(e) => {
			log_error!("Unable to mount /system from {}: {:?}", sysdisk, e);
			return ;
			},
		},
	}

	// 2. Symbolic link /sysroot to the specified folder
	let sysroot = ::config::get_string(::config::Value::SysRoot);
	handle::Dir::open(Path::new("/")).unwrap().symlink("sysroot", Path::new(sysroot));

	// *. Testing: open a file known to exist on the testing disk
	{
		match handle::File::open( Path::new("/system/1.TXT"), handle::FileOpenMode::SharedRO )
		{
		Err(e) => log_warning!("VFS test file can't be opened: {:?}", e),
		Ok(h) => {
			log_debug!("VFS open test = {:?}", h);
			let mut buf = [0; 16];
			let sz = h.read(0, &mut buf).unwrap();
			log_debug!("- Contents: {:?}", ::lib::RawString(&buf[..sz]));
			},
		}

		// - Iterate root dir
		match handle::Dir::open( Path::new("/") )
		{
		Err(e) => log_warning!("VFS root cannot be opened: {:?}", e),
		Ok(h) =>
			for name in h.iter() {
				log_log!("{}: {:?}", 0, name);
			},
		}
		// - Iterate /system
		match handle::Dir::open( Path::new("/system") )
		{
		Err(e) => log_warning!("System volume cannot be opened: {:?}", e),
		Ok(h) =>
			for name in h.iter() {
				log_log!("{:?}", name);
			},
		}
	}

}

// vim: ft=rust

<commit_msg>Kernel/main - Add automount for testing purposes<commit_after>// "Tifflin" Kernel
// - By John Hodge (thePowersGang)
//
// Core/main.rs
// - Kernel main
#![crate_name="kernel"]
#![crate_type="lib"]
#![feature(no_std)]
#![feature(asm)]	// Enables the asm! syntax extension
#![feature(box_syntax)]	// Enables 'box' syntax
#![feature(thread_local)]	// Allows use of thread_local
#![feature(lang_items)]	// Allow definition of lang_items
#![feature(core)]	// silences warnings about write!
#![feature(optin_builtin_traits)]	// Negative impls
#![feature(unique)]	// Unique
#![feature(slice_patterns)]	// Slice (array) destructuring patterns, used by multiboot code
#![feature(step_by)]	// Range::step_by
#![feature(linkage)]	// allows using #[linkage="external"]
#![feature(const_fn)]	// Allows defining `const fn`
#![no_std]

#![feature(plugin)]
#![feature(custom_attribute)]
#![plugin(tag_safe)]
use prelude::*;

#[macro_use]
extern crate core;

pub use arch::memory::PAGE_SIZE;

#[doc(hidden)]
#[macro_use] pub mod logmacros;
#[doc(hidden)]
#[macro_use] pub mod macros;
#[doc(hidden)]
#[macro_use] #[cfg(arch__amd64)] #[path="arch/amd64/mod-macros.rs"] pub mod arch_macros;

// Evil Hack: For some reason, write! (and friends) will expand pointing to std instead of core
#[doc(hidden)]
mod std {
	pub use core::option;
	pub use core::{default,fmt,cmp};
	pub use core::marker;	// needed for derive(Copy)
	pub use core::iter;	// needed for 'for'
}

/// Kernel's version of 'std::prelude'
pub mod prelude;

/// Library datatypes (Vec, Queue, ...)
#[macro_use]
pub mod lib;	// Clone of libstd

/// Heavy synchronisation primitives (Mutex, Semaphore, RWLock, ...)
#[macro_use]
pub mod sync;

/// Asynchrnous wait support
pub mod async;

/// Logging framework
pub mod logging;
/// Memory management (physical, virtual, heap)
pub mod memory;
/// Thread management
#[macro_use]
pub mod threads;
/// Timekeeping (timers and wall time)
pub mod time;

// Module/Executable loading (and symbol lookup)
pub mod loading;
/// Module management (loading and initialisation of kernel modules)
pub mod modules;

/// Meta devices (the Hardware Abstraction Layer)
pub mod metadevs;
/// Device to driver mapping manager
///
/// Starts driver instances for the devices it sees
pub mod device_manager;

/// User output, via a kernel-provided compositing "WM"
pub mod gui;

// Public for driver modules
pub mod vfs;

mod config;

/// Stack unwinding (panic) handling
pub mod unwind;

pub mod irqs;

/// Built-in device drivers
mod hw;

/// Achitecture-specific code - AMD64 (aka x86-64)
#[macro_use]
#[cfg(arch__amd64)] #[path="arch/amd64/mod.rs"] pub mod arch;	// Needs to be pub for exports to be avaliable

/// Kernel entrypoint
#[no_mangle]
pub extern "C" fn kmain()
{
	log_notice!("Tifflin Kernel v{} build {} starting", env!("TK_VERSION"), env!("TK_BUILD"));
	log_notice!("> Git state : {}", env!("TK_GITSPEC"));
	log_notice!("> Built with {}", env!("RUST_VERSION"));

	// Initialise core services before attempting modules
	::memory::phys::init();
	::memory::virt::init();
	::memory::heap::init();
	::threads::init();

	log_log!("Command line = '{}'", ::arch::boot::get_boot_string());
	// TODO: Parse command line into config

	// Dump active video mode
	let vidmode = ::arch::boot::get_video_mode();
	match vidmode {
	Some(m) => {
		log_debug!("Video mode : {}x{} @ {:#x}", m.width, m.height, m.base);
		::metadevs::video::set_boot_mode(m);
		},
	None => log_debug!("No video mode present")
	}

	// Modules (dependency tree included)
	// - Requests that the GUI be started as soon as possible
	::modules::init(&["GUI"]);

	// Yield to allow init threads to run
	::threads::yield_time();

	// Run system init
	sysinit();

	// Thread 0 idle loop
	log_info!("Entering idle");
	loop
	{
		log_trace!("TID0 napping");
		::threads::yield_time();
	}
}

// Initialise the system once drivers are up
fn sysinit()
{
	use metadevs::storage::VolumeHandle;
	use vfs::{mount,handle};
	use vfs::Path;

	// 1. Mount /system to the specified volume
	let sysdisk = ::config::get_string(::config::Value::SysDisk);
	match VolumeHandle::open_named(sysdisk)
	{
	Err(e) => {
		log_error!("Unable to open /system volume {}: {}", sysdisk, e);
		return ;
		},
	Ok(vh) => match mount::mount("/system".as_ref(), vh, "", &[])
		{
		Ok(_) => {},
		Err(e) => {
			log_error!("Unable to mount /system from {}: {:?}", sysdisk, e);
			return ;
			},
		},
	}

	// 2. Symbolic link /sysroot to the specified folder
	let sysroot = ::config::get_string(::config::Value::SysRoot);
	handle::Dir::open(Path::new("/")).unwrap().symlink("sysroot", Path::new(sysroot));

	// *. Testing: open a file known to exist on the testing disk
	{
		match handle::File::open( Path::new("/system/1.TXT"), handle::FileOpenMode::SharedRO )
		{
		Err(e) => log_warning!("VFS test file can't be opened: {:?}", e),
		Ok(h) => {
			log_debug!("VFS open test = {:?}", h);
			let mut buf = [0; 16];
			let sz = h.read(0, &mut buf).unwrap();
			log_debug!("- Contents: {:?}", ::lib::RawString(&buf[..sz]));
			},
		}

		// - Iterate root dir
		match handle::Dir::open( Path::new("/") )
		{
		Err(e) => log_warning!("VFS root cannot be opened: {:?}", e),
		Ok(h) =>
			for name in h.iter() {
				log_log!("{}: {:?}", 0, name);
			},
		}
		// - Iterate /system
		match handle::Dir::open( Path::new("/system") )
		{
		Err(e) => log_warning!("System volume cannot be opened: {:?}", e),
		Ok(h) =>
			for name in h.iter() {
				log_log!("{:?}", name);
			},
		}
	}

	// *. TEST Automount
	// - Probably shouldn't be included in the final version, but works for testing filesystem and storage drivers
	handle::Dir::open( Path::new("/") ).and_then(|h| h.mkdir("mount")).unwrap();
	for (_,v) in ::metadevs::storage::enum_lvs()
	{
		let vh = match VolumeHandle::open_named(&v)
			{
			Err(e) => {
				log_log!("Unable to open '{}': {}", v, e);
				continue;
				},
			Ok(v) => v,
			};
		handle::Dir::open( Path::new("/mount") ).and_then(|h| h.mkdir(&v)).unwrap();
		let mountpt = format!("/mount/{}",v);
		match mount::mount( mountpt.as_ref(), vh, "", &[] )
		{
		Ok(_) => log_log!("Auto-mounted to {}", mountpt),
		Err(e) => log_notice!("Unable to automount '{}': {:?}", v, e),
		}

	}
}

// vim: ft=rust

<|endoftext|>


<commit_before>use std::collections::btree_map::BTreeMap;
use js_types::js_type::{JsType, JsThing};

pub struct JsObj {
    proto: Option<Box<JsObj>>,
    dict: BTreeMap<JsType, JsType>,
}

impl JsThing for JsObj {}

impl JsObj {
    pub fn new() -> JsObj {
        JsObj {
            proto: None,
            dict: BTreeMap::new(),
        }
    }
}
<commit_msg>Use HashMap in JsObj instead of BTreeMap<commit_after>use std::collections::hash_map::HashMap;
use js_types::js_type::{JsType, JsThing};

pub struct JsObj {
    proto: Option<Box<JsObj>>,
    dict: HashMap<JsType, JsType>,
}

impl JsThing for JsObj {}

impl JsObj {
    pub fn new() -> JsObj {
        JsObj {
            proto: None,
            dict: HashMap::new(),
        }
    }
}
<|endoftext|>


<commit_before>// Implements http://rosettacode.org/wiki/Call_a_foreign-language_function
// not_tested

extern crate libc;

use libc::c_char;
use std::c_str::CString;

extern "C" {
    // C functions are declared in an `extern "C"` block.
    fn strdup(s: *c_char) -> *c_char;
}

fn main() {
    // Create a Rust static string. No allocations.
    let rust_str = "Hello World!";
    // Call strdup. C functions are considered possibly unsafe to call, so
    // an unsafe block is needed.
    let dup_c_str = unsafe {
        // Use the `with_c_str` method to create a nul-terminated C string. This
        // results in an allocation in order to add the nul. If you placed an
        // explicit nul inside the Rust string, you could avoid the allocation
        // by using the `with_c_str_unchecked` method. Both methods accept a
        // closure that is called with the nul-terminated C string.
        rust_str.with_c_str(|c_str| {
            strdup(c_str)
        })
    };
    // Wrap the C string in the `CString` struct. We instruct the `CString` to
    // take ownership of the C string, so it will free it automatically when
    // the scope ends using the C `free` function. Creating a `CString` is
    // possibly unsafe because you could pass it an invalid address.
    let wrap_dup_c_str = unsafe {
        CString::new(dup_c_str, true)
    };
    // Get a Rust string slice out of the wrapper. The `as_str` method returns
    // Option<&str> because you may have passed invalid UTF-8 encoded C string
    // to `CString`. This does not allocate memory.
    let dup_rust_str = wrap_dup_c_str.as_str().unwrap();
    // Now you can easily print the result
    println!("{}", dup_rust_str);
    // The block ends here, and `CString` frees the C string we created.
}
<commit_msg>`*T` type has been removed from the language; use `*const T` instead.<commit_after>// Implements http://rosettacode.org/wiki/Call_a_foreign-language_function
// not_tested

extern crate libc;

use libc::c_char;
use std::c_str::CString;

extern "C" {
    // C functions are declared in an `extern "C"` block.
    fn strdup(s: *const c_char) -> *const c_char;
}

fn main() {
    // Create a Rust static string. No allocations.
    let rust_str = "Hello World!";
    // Call strdup. C functions are considered possibly unsafe to call, so
    // an unsafe block is needed.
    let dup_c_str = unsafe {
        // Use the `with_c_str` method to create a nul-terminated C string. This
        // results in an allocation in order to add the nul. If you placed an
        // explicit nul inside the Rust string, you could avoid the allocation
        // by using the `with_c_str_unchecked` method. Both methods accept a
        // closure that is called with the nul-terminated C string.
        rust_str.with_c_str(|c_str| {
            strdup(c_str)
        })
    };
    // Wrap the C string in the `CString` struct. We instruct the `CString` to
    // take ownership of the C string, so it will free it automatically when
    // the scope ends using the C `free` function. Creating a `CString` is
    // possibly unsafe because you could pass it an invalid address.
    let wrap_dup_c_str = unsafe {
        CString::new(dup_c_str, true)
    };
    // Get a Rust string slice out of the wrapper. The `as_str` method returns
    // Option<&str> because you may have passed invalid UTF-8 encoded C string
    // to `CString`. This does not allocate memory.
    let dup_rust_str = wrap_dup_c_str.as_str().unwrap();
    // Now you can easily print the result
    println!("{}", dup_rust_str);
    // The block ends here, and `CString` frees the C string we created.
}
<|endoftext|>


<commit_before>use std::io;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::mem;
use std::collections::HashSet;

use clap::{App, Arg, ArgMatches};
use hyper::method::Method;
use mime;
use multipart::client::Multipart;
use serde_json;
use walkdir::{WalkDir, Iter as WalkDirIter};
use zip;

use CliResult;
use utils::{TempFile, get_org_and_project, get_sha1_checksum};
use macho::is_macho_file;
use commands::Config;

const BATCH_SIZE : usize = 15;

#[derive(Debug, Deserialize)]
struct DSymFile {
    uuid: String,
    #[serde(rename="objectName")]
    object_name: String,
    #[serde(rename="cpuName")]
    cpu_name: String,
}

#[derive(Deserialize)]
struct MissingChecksumsResponse {
    missing: HashSet<String>,
}

struct LocalFile {
    path: PathBuf,
    arc_name: String,
    checksum: String,
}

struct BatchIter {
    path: PathBuf,
    wd_iter: WalkDirIter,
    batch: Vec<LocalFile>,
}

impl BatchIter {
    pub fn new<P: AsRef<Path>>(path: P) -> BatchIter {
        BatchIter {
            path: path.as_ref().to_path_buf(),
            wd_iter: WalkDir::new(&path).into_iter(),
            batch: vec![],
        }
    }
}

impl Iterator for BatchIter {
    type Item = CliResult<Vec<LocalFile>>;

    fn next(&mut self) -> Option<CliResult<Vec<LocalFile>>> {
        loop {
            if let Some(dent_res) = self.wd_iter.next() {
                let dent = iter_try!(dent_res);
                let md = iter_try!(dent.metadata());
                if md.is_file() && is_macho_file(dent.path()) {
                    let name = Path::new("DebugSymbols")
                        .join(dent.path().strip_prefix(&self.path).unwrap());
                    println!("  {}", name.display());
                    self.batch.push(LocalFile {
                        path: dent.path().to_path_buf(),
                        arc_name: name.to_string_lossy().into_owned(),
                        checksum: iter_try!(get_sha1_checksum(dent.path())),
                    });
                    if self.batch.len() > BATCH_SIZE {
                        break;
                    }
                }
            } else {
                break;
            }
        }
        if self.batch.len() == 0 {
            None
        } else {
            Some(Ok(mem::replace(&mut self.batch, vec![])))
        }
    }
}

fn find_missing_files(config: &Config, files: Vec<LocalFile>, api_path: &str)
    -> CliResult<Vec<LocalFile>>
{
    let mut url = format!("{}unknown/?", api_path);
    for (idx, ref file) in files.iter().enumerate() {
        if idx > 0 {
            url.push('&');
        }
        url.push_str("checksums=");
        url.push_str(&file.checksum);
    }
    let mut resp = try!(config.api_request(Method::Get, &url));

    // This happens if the sentry installation we're connecting to does not
    // have that endpoint.  In that case just continue.  Any other HTTP
    // failure is ignored here too which is okay since we will try to upload
    // next step anyways.
    if !resp.status.is_success() {
        return Ok(files);
    }

    let state : MissingChecksumsResponse = try!(
        serde_json::from_reader(&mut resp));
    let mut rv = vec![];
    for file in files.into_iter() {
        if state.missing.contains(&file.checksum) {
            rv.push(file)
        }
    }
    Ok(rv)
}

fn zip_up(files: &[LocalFile]) -> CliResult<TempFile> {
    println!("  >> Uploading a batch of missing files ...");
    let tf = try!(TempFile::new());
    let mut zip = zip::ZipWriter::new(tf.open());
    for ref file in files {
        println!("  >>   {}", file.arc_name);
        try!(zip.start_file(file.arc_name.clone(),
            zip::CompressionMethod::Deflated));
        try!(io::copy(&mut try!(File::open(file.path.clone())), &mut zip));
    }
    Ok(tf)
}

fn upload_dsyms(files: &[LocalFile], config: &Config,
                api_path: &str) -> CliResult<Vec<DSymFile>> {
    let tf = try!(zip_up(files));
    let req = try!(config.prepare_api_request(Method::Post, api_path));
    let mut mp = try!(Multipart::from_request_sized(req));
    mp.write_stream("file", &mut tf.open(), Some("archive.zip"),
        "application/zip".parse::<mime::Mime>().ok());
    let mut resp = try!(mp.send());
    Ok(try!(serde_json::from_reader(&mut resp)))
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b>
{
    app
        .about("uploads debug symbols to a project")
        .arg(Arg::with_name("org")
             .value_name("ORG")
             .long("org")
             .short("o")
             .help("The organization slug"))
        .arg(Arg::with_name("project")
             .value_name("PROJECT")
             .long("project")
             .short("p")
             .help("The project slug"))
        .arg(Arg::with_name("global")
             .long("global")
             .short("g")
             .help("Uploads the dsyms globally. This can only be done \
                    with super admin access for the Sentry installation"))
        .arg(Arg::with_name("path")
             .value_name("PATH")
             .help("The path to the debug symbols")
             .required(true)
             .index(1))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> CliResult<()> {
    let path = matches.value_of("path").unwrap();
    let api_path = if matches.is_present("global") {
        "/system/global-dsyms/".to_owned()
    } else {
        let (org, project) = try!(get_org_and_project(matches));
        format!("/projects/{}/{}/files/dsyms/", org, project)
    };

    println!("Uploading symbols from {}...", path);

    for batch_res in BatchIter::new(path) {
        let batch = try!(batch_res);
        let missing = try!(find_missing_files(config, batch, &api_path));
        if missing.len() == 0 {
            continue;
        }
        println!("Uploading archive ...");
        let rv = try!(upload_dsyms(&missing, config, &api_path));
        if rv.len() == 0 {
            fail!("Server did not accept any debug symbols.");
        } else {
            println!("Accepted debug symbols:");
            for df in rv {
                println!("  {} ({}; {})", df.uuid, df.object_name, df.cpu_name);
            }
        }
    }

    Ok(())
}
<commit_msg>Improve output for upload dsym command<commit_after>use std::io;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::mem;
use std::collections::HashSet;

use clap::{App, Arg, ArgMatches};
use hyper::method::Method;
use mime;
use multipart::client::Multipart;
use serde_json;
use walkdir::{WalkDir, Iter as WalkDirIter};
use zip;

use CliResult;
use utils::{TempFile, get_org_and_project, get_sha1_checksum};
use macho::is_macho_file;
use commands::Config;

const BATCH_SIZE : usize = 15;

#[derive(Debug, Deserialize)]
struct DSymFile {
    uuid: String,
    #[serde(rename="objectName")]
    object_name: String,
    #[serde(rename="cpuName")]
    cpu_name: String,
}

#[derive(Deserialize)]
struct MissingChecksumsResponse {
    missing: HashSet<String>,
}

struct LocalFile {
    path: PathBuf,
    arc_name: String,
    checksum: String,
}

struct BatchIter {
    path: PathBuf,
    wd_iter: WalkDirIter,
    batch: Vec<LocalFile>,
}

impl BatchIter {
    pub fn new<P: AsRef<Path>>(path: P) -> BatchIter {
        BatchIter {
            path: path.as_ref().to_path_buf(),
            wd_iter: WalkDir::new(&path).into_iter(),
            batch: vec![],
        }
    }
}

impl Iterator for BatchIter {
    type Item = CliResult<Vec<LocalFile>>;

    fn next(&mut self) -> Option<CliResult<Vec<LocalFile>>> {
        loop {
            if let Some(dent_res) = self.wd_iter.next() {
                let dent = iter_try!(dent_res);
                let md = iter_try!(dent.metadata());
                if md.is_file() && is_macho_file(dent.path()) {
                    let name = Path::new("DebugSymbols")
                        .join(dent.path().strip_prefix(&self.path).unwrap());
                    println!("  {}", name.display());
                    self.batch.push(LocalFile {
                        path: dent.path().to_path_buf(),
                        arc_name: name.to_string_lossy().into_owned(),
                        checksum: iter_try!(get_sha1_checksum(dent.path())),
                    });
                    if self.batch.len() > BATCH_SIZE {
                        break;
                    }
                }
            } else {
                break;
            }
        }
        if self.batch.len() == 0 {
            None
        } else {
            Some(Ok(mem::replace(&mut self.batch, vec![])))
        }
    }
}

fn find_missing_files(config: &Config, files: Vec<LocalFile>, api_path: &str)
    -> CliResult<Vec<LocalFile>>
{
    let mut url = format!("{}unknown/?", api_path);
    for (idx, ref file) in files.iter().enumerate() {
        if idx > 0 {
            url.push('&');
        }
        url.push_str("checksums=");
        url.push_str(&file.checksum);
    }
    let mut resp = try!(config.api_request(Method::Get, &url));

    // This happens if the sentry installation we're connecting to does not
    // have that endpoint.  In that case just continue.  Any other HTTP
    // failure is ignored here too which is okay since we will try to upload
    // next step anyways.
    if !resp.status.is_success() {
        return Ok(files);
    }

    let state : MissingChecksumsResponse = try!(
        serde_json::from_reader(&mut resp));
    let mut rv = vec![];
    for file in files.into_iter() {
        if state.missing.contains(&file.checksum) {
            rv.push(file)
        }
    }
    Ok(rv)
}

fn zip_up(files: &[LocalFile]) -> CliResult<TempFile> {
    println!("  Uploading a batch of missing files ...");
    let tf = try!(TempFile::new());
    let mut zip = zip::ZipWriter::new(tf.open());
    for ref file in files {
        println!("    {}", file.arc_name);
        try!(zip.start_file(file.arc_name.clone(),
            zip::CompressionMethod::Deflated));
        try!(io::copy(&mut try!(File::open(file.path.clone())), &mut zip));
    }
    Ok(tf)
}

fn upload_dsyms(files: &[LocalFile], config: &Config,
                api_path: &str) -> CliResult<Vec<DSymFile>> {
    let tf = try!(zip_up(files));
    let req = try!(config.prepare_api_request(Method::Post, api_path));
    let mut mp = try!(Multipart::from_request_sized(req));
    mp.write_stream("file", &mut tf.open(), Some("archive.zip"),
        "application/zip".parse::<mime::Mime>().ok());
    let mut resp = try!(mp.send());
    Ok(try!(serde_json::from_reader(&mut resp)))
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b>
{
    app
        .about("uploads debug symbols to a project")
        .arg(Arg::with_name("org")
             .value_name("ORG")
             .long("org")
             .short("o")
             .help("The organization slug"))
        .arg(Arg::with_name("project")
             .value_name("PROJECT")
             .long("project")
             .short("p")
             .help("The project slug"))
        .arg(Arg::with_name("global")
             .long("global")
             .short("g")
             .help("Uploads the dsyms globally. This can only be done \
                    with super admin access for the Sentry installation"))
        .arg(Arg::with_name("path")
             .value_name("PATH")
             .help("The path to the debug symbols")
             .required(true)
             .index(1))
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> CliResult<()> {
    let path = matches.value_of("path").unwrap();
    let api_path = if matches.is_present("global") {
        "/system/global-dsyms/".to_owned()
    } else {
        let (org, project) = try!(get_org_and_project(matches));
        format!("/projects/{}/{}/files/dsyms/", org, project)
    };

    println!("Uploading symbols from {}...", path);

    for batch_res in BatchIter::new(path) {
        let batch = try!(batch_res);
        let missing = try!(find_missing_files(config, batch, &api_path));
        if missing.len() == 0 {
            continue;
        }
        println!("Detected missing files");
        let rv = try!(upload_dsyms(&missing, config, &api_path));
        if rv.len() > 0 {
            println!("  Accepted debug symbols:");
            for df in rv {
                println!("    {} ({}; {})", df.uuid, df.object_name, df.cpu_name);
            }
        }
    }

    Ok(())
}
<|endoftext|>