<commit_before>use std::hashmap::HashMap;
mod simplepattern_test;

struct ParameterDictionary {
    params: HashMap<~str, ~[~str]>
}

impl ParameterDictionary {
    fn new() -> ParameterDictionary {
        ParameterDictionary{params: HashMap::new()}
    }
    fn add(&mut self, key: &str, value: &str) {
        let v=self.params.find_or_insert(key.into_owned(), ~[]);
        v.push(value.into_owned());
    }
}
impl Index<~str, ~str> for ParameterDictionary {
    fn index(&self, rhs: &~str) -> ~str {
        self.params.get(rhs).head().clone()
    }
}

pub trait PatternMatcher {
    fn matches(&self, input: &str) -> MatchResult;
}

struct MatchResult {
    is_match: bool,
    params: ParameterDictionary
}

pub struct SimplePattern {
    pattern: ~str,
}

impl SimplePattern {
    fn new(pattern: &str) -> SimplePattern {
        SimplePattern { pattern: pattern.into_owned()}
    }
}

impl PatternMatcher for SimplePattern {
    fn matches(&self, input: &str) -> MatchResult {
        let res=input == self.pattern;
        MatchResult{is_match: res, params: ParameterDictionary::new()}
    }
}

<commit_msg>I have to convert all this C# string manipulation to Rust. Send help.<commit_after>use std::hashmap::HashMap;
mod simplepattern_test;

struct ParameterDictionary {
    params: HashMap<~str, ~[~str]>
}

impl ParameterDictionary {
    fn new() -> ParameterDictionary {
        ParameterDictionary{params: HashMap::new()}
    }
    fn add(&mut self, key: &str, value: &str) {
        let v=self.params.find_or_insert(key.into_owned(), ~[]);
        v.push(value.into_owned());
    }
}
impl Index<~str, ~str> for ParameterDictionary {
    fn index(&self, rhs: &~str) -> ~str {
        self.params.get(rhs).head().clone()
    }
}

pub trait PatternMatcher {
    fn matches(&self, input: &str) -> MatchResult;
}

struct MatchResult {
    is_match: bool,
    params: ParameterDictionary //the contents of this are undefined when not matching
}

impl MatchResult {
    fn a_match(params: ParameterDictionary) -> MatchResult {
        MatchResult{is_match: true, params: params}
    }
    fn no_match() -> MatchResult{
        MatchResult{is_match: false, params: ParameterDictionary::new()}
    }
}

/*
        private List<Group> Groups;

        private class Group {
            public string ParamName;
            public string Text;
            public bool IsParam=false;
            public List<string> ValidMatches=new List<string>();
            public bool MatchAll=true;
            public bool Optional=false;
            public char End;
            public Regex MatchType=null;
        }
*/

priv struct PatternGroup{
    name: Option<~str>,
    text: Option<~str>,
    valid_values: ~[~str],
    match_all: bool,
    end: char,
}

pub struct SimplePattern {
    pattern: ~str,
    groups: ~[PatternGroup]
}

impl SimplePattern {
    fn new(pattern: &str) -> SimplePattern {
        SimplePattern { pattern: pattern.into_owned(), groups: ~[]}
    }
    /*This will update the "groups", which is basically a way to pre-cache and break apart the important parts of the pattern*/
    fn update_groups(&mut self){
        let mut i=0;
        let p=&self.pattern.clone(); //might be mut?
        for c in p.iter() {
            if(c == '{') {

            }
        }
        /*while(i<p.len()){
            if(p[i] == '{'){

            }
            i+=1;
        }*/
    }
}

impl PatternMatcher for SimplePattern {
    fn matches(&self, input: &str) -> MatchResult {
        let res=input == self.pattern;
        MatchResult{is_match: res, params: ParameterDictionary::new()}
    }

}

/*
 /** This will parse the Pattern string one group at a time. **
        int ParseParam (int start, ref Group g)
        {
            start++;

            int end=Pattern.Substring(start).IndexOf('}')+start;
            if(end+1>=Pattern.Length-1){
                g.End='\0';
            }else{
                g.End=Pattern[end+1];
            }
            string p=CutString(Pattern,start,end);
            g.Text=p;
            int tmp=p.IndexOf('[');
            if(tmp==-1){ //not found. Just trim it up and get the paramname
                p=p.Trim();
                if(p=="*"){
                    g.Optional=true; //meh. Still add it as a match-all group for the hell of it
                }
                g.MatchAll=true;
            }else{
                //return end;
                g.MatchAll=false;
                string l=CutString(p,tmp+1,p.IndexOf(']'));
                l=l.Replace(" ","");
                p=p.Substring(0,p.IndexOf("=")).Trim();
                int count=0;
                while(true){
                    if(l.Length==0){
                        break;
                    }
                    int endm=l.IndexOf(',');
                    if(endm==-1){
                        endm=l.Length;
                        g.ValidMatches.Add(l);
                        break;
                    }
                    g.ValidMatches.Add(l.Remove(endm));
                    l=l.Substring(endm+1);
                    count++;
                    if(count>100){
                        throw new ApplicationException("inifinite loop detected");
                    }
                }
            }

            g.ParamName=p;
            return end;
        }
     /** This will update all of the "groups" or parameter names/values for the pattern string. *
        private void UpdateGroups ()
        {
            List<Group> groups = new List<Group> ();
            Group g=new Group();
            for(int i=0;i<Pattern.Length;i++){
                if(Pattern[i]=='{'){
                    if(g!=null) //g will never be null
                        groups.Add(g);
                    g=new Group();
                    g.IsParam=true;
                    i=ParseParam(i,ref g);
                    groups.Add(g);
                    g=null;
                }else if(g==null){
                    g=new Group();
                    g.IsParam=false;
                    g.Text+=Pattern[i];
                }else{
                    g.Text+=Pattern[i];
                }
            }
            if(g!=null){
                groups.Add(g);
            }
            Groups=groups;
        }
    a}
        /**Little helper method to cut a string from start to end point. Just shorter than typing .Remove(end).Substring(start) **
        private string CutString(string s,int start,int end){
            return s.Remove(end).Substring(start);
        }
*/

<|endoftext|>


<commit_before>//! Runtime services
//!
//! The `rt` module provides a narrow set of runtime services,
//! including the global heap (exported in `heap`) and unwinding and
//! backtrace support. The APIs in this module are highly unstable,
//! and should be considered as private implementation details for the
//! time being.

#![unstable(
    feature = "rt",
    reason = "this public module should not exist and is highly likely \
              to disappear",
    issue = "none"
)]
#![doc(hidden)]

// Re-export some of our utilities which are expected by other crates.
pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count};

// To reduce the generated code of the new `lang_start`, this function is doing
// the real work.
#[cfg(not(test))]
fn lang_start_internal(
    main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
    argc: isize,
    argv: *const *const u8,
) -> Result<isize, !> {
    use crate::{mem, panic, sys, sys_common};
    let rt_abort = move |e| {
        mem::forget(e);
        rtabort!("initialization or cleanup bug");
    };
    // Guard against the code called by this function from unwinding outside of the Rust-controlled
    // code, which is UB. This is a requirement imposed by a combination of how the
    // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking
    // mechanism itself.
    //
    // There are a couple of instances where unwinding can begin. First is inside of the
    // `rt::init`, `rt::cleanup` and similar functions controlled by libstd. In those instances a
    // panic is a libstd implementation bug. A quite likely one too, as there isn't any way to
    // prevent libstd from accidentally introducing a panic to these functions. Another is from
    // user code from `main` or, more nefariously, as described in e.g. issue #86030.
    // SAFETY: Only called once during runtime initialization.
    panic::catch_unwind(move || unsafe { sys_common::rt::init(argc, argv) }).map_err(rt_abort)?;
    let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
        .map_err(move |e| {
            mem::forget(e);
            rtprintpanic!("drop of the panic payload panicked");
            sys::abort_internal()
        });
    panic::catch_unwind(sys_common::rt::cleanup).map_err(rt_abort)?;
    ret_code
}

#[cfg(not(test))]
#[lang = "start"]
fn lang_start<T: crate::process::Termination + 'static>(
    main: fn() -> T,
    argc: isize,
    argv: *const *const u8,
) -> isize {
    lang_start_internal(
        &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report(),
        argc,
        argv,
    )
    .into_ok()
}
<commit_msg>Avoid codegen for Result::into_ok in lang_start<commit_after>//! Runtime services
//!
//! The `rt` module provides a narrow set of runtime services,
//! including the global heap (exported in `heap`) and unwinding and
//! backtrace support. The APIs in this module are highly unstable,
//! and should be considered as private implementation details for the
//! time being.

#![unstable(
    feature = "rt",
    reason = "this public module should not exist and is highly likely \
              to disappear",
    issue = "none"
)]
#![doc(hidden)]

// Re-export some of our utilities which are expected by other crates.
pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count};

// To reduce the generated code of the new `lang_start`, this function is doing
// the real work.
#[cfg(not(test))]
fn lang_start_internal(
    main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
    argc: isize,
    argv: *const *const u8,
) -> Result<isize, !> {
    use crate::{mem, panic, sys, sys_common};
    let rt_abort = move |e| {
        mem::forget(e);
        rtabort!("initialization or cleanup bug");
    };
    // Guard against the code called by this function from unwinding outside of the Rust-controlled
    // code, which is UB. This is a requirement imposed by a combination of how the
    // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking
    // mechanism itself.
    //
    // There are a couple of instances where unwinding can begin. First is inside of the
    // `rt::init`, `rt::cleanup` and similar functions controlled by libstd. In those instances a
    // panic is a libstd implementation bug. A quite likely one too, as there isn't any way to
    // prevent libstd from accidentally introducing a panic to these functions. Another is from
    // user code from `main` or, more nefariously, as described in e.g. issue #86030.
    // SAFETY: Only called once during runtime initialization.
    panic::catch_unwind(move || unsafe { sys_common::rt::init(argc, argv) }).map_err(rt_abort)?;
    let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
        .map_err(move |e| {
            mem::forget(e);
            rtprintpanic!("drop of the panic payload panicked");
            sys::abort_internal()
        });
    panic::catch_unwind(sys_common::rt::cleanup).map_err(rt_abort)?;
    ret_code
}

#[cfg(not(test))]
#[lang = "start"]
fn lang_start<T: crate::process::Termination + 'static>(
    main: fn() -> T,
    argc: isize,
    argv: *const *const u8,
) -> isize {
    let Ok(v) = lang_start_internal(
        &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report(),
        argc,
        argv,
    );
    v
}
<|endoftext|>


<commit_before>//!
//! Implementation of the `Session` trait through various implementations
//! automatically choosing the best available interface.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call `AutoSession::new`. A new session will be opened, if the
//! any available interface is successful and will be closed once the `AutoSessionNotifier` is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other objects that need it to open devices themselves.
//! The `AutoSession` is clonable and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via `AutoSession::change_vt`) and to automatically enable it again,
//! when the session becomes active again.
//!
//! It is crutial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
//! automatically by the `UdevBackend`, if not done manually).
//! ```

use super::{AsErrno, AsSessionObserver, Session, SessionNotifier, SessionObserver};
use super::direct::{self, direct_session_bind, DirectSession, DirectSessionNotifier};
#[cfg(feature = "backend_session_logind")]
use super::logind::{self, logind_session_bind, BoundLogindSession, LogindSession, LogindSessionNotifier};
use nix::fcntl::OFlag;
use std::cell::RefCell;
use std::io::Error as IoError;
use std::os::unix::io::RawFd;
use std::path::Path;
use std::rc::Rc;
use wayland_server::EventLoopHandle;
use wayland_server::sources::{EventSource, SignalEventSource};

/// `Session` using the best available inteface
#[derive(Clone)]
pub enum AutoSession {
    /// Logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSession),
    /// Direct / tty session
    Direct(Rc<RefCell<DirectSession>>),
}

/// `SessionNotifier` using the best available inteface
pub enum AutoSessionNotifier {
    /// Logind session nofifier
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSessionNotifier),
    /// Direct / tty session notifier
    Direct(DirectSessionNotifier),
}

/// Bound session that is driven by the `wayland_server::EventLoop`.
///
/// See `auto_session_bind` for details.
///
/// Dropping this object will close the session just like the `AutoSessionNotifier`.
pub enum BoundAutoSession {
    /// Bound logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(BoundLogindSession),
    /// Bound direct / tty session
    Direct(SignalEventSource<DirectSessionNotifier>),
}

/// Id's used by the `AutoSessionNotifier` internally.
#[derive(PartialEq, Eq)]
pub struct AutoId(AutoIdInternal);
#[derive(PartialEq, Eq)]
enum AutoIdInternal {
    #[cfg(feature = "backend_session_logind")]
    Logind(logind::Id),
    Direct(direct::Id),
}

impl AutoSession {
    /// Tries to create a new session via the best available interface.
    #[cfg(feature = "backend_session_logind")]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = ::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create logind session");
        match LogindSession::new(logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Logind(session),
                AutoSessionNotifier::Logind(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create logind session: {}", err);
                info!(logger, "Falling back to create tty session");
                match DirectSession::new(None, logger.clone()) {
                    Ok((session, notifier)) => Some((
                        AutoSession::Direct(Rc::new(RefCell::new(session))),
                        AutoSessionNotifier::Direct(notifier),
                    )),
                    Err(err) => {
                        warn!(logger, "Failed to create direct session: {}", err);
                        error!(
                            logger,
                            "Could not create any session, possibilities exhausted"
                        );
                        None
                    }
                }
            }
        }
    }

    #[cfg(not(feature = "backend_session_logind"))]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = ::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create tty session");
        match DirectSession::new(None, logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Direct(Rc::new(RefCell::new(session))),
                AutoSessionNotifier::Direct(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create direct session: {}", err);
                error!(
                    logger,
                    "Could not create any session, possibilities exhausted"
                );
                None
            }
        }
    }
}

/// Bind an `AutoSessionNotifier` to an `EventLoop`.
///
/// Allows the `AutoSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
/// session state and call it's `SessionObservers`.
pub fn auto_session_bind(
    notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle
) -> ::std::result::Result<BoundAutoSession, (IoError, AutoSessionNotifier)> {
    Ok(match notifier {
        #[cfg(feature = "backend_session_logind")]
        AutoSessionNotifier::Logind(logind) => BoundAutoSession::Logind(logind_session_bind(logind, evlh)
            .map_err(|(error, notifier)| (error, AutoSessionNotifier::Logind(notifier)))?),
        AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, evlh)
            .map_err(|(error, notifier)| (error, AutoSessionNotifier::Direct(notifier)))?),
    })
}

impl Session for AutoSession {
    type Error = Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &mut AutoSession::Logind(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
            &mut AutoSession::Direct(ref mut direct) => direct.open(path, flags).map_err(|e| e.into()),
        }
    }
    fn close(&mut self, fd: RawFd) -> Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &mut AutoSession::Logind(ref mut logind) => logind.close(fd).map_err(|e| e.into()),
            &mut AutoSession::Direct(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
        }
    }

    fn change_vt(&mut self, vt: i32) -> Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &mut AutoSession::Logind(ref mut logind) => logind.change_vt(vt).map_err(|e| e.into()),
            &mut AutoSession::Direct(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
        }
    }

    fn is_active(&self) -> bool {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &AutoSession::Logind(ref logind) => logind.is_active(),
            &AutoSession::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> String {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &AutoSession::Logind(ref logind) => logind.seat(),
            &AutoSession::Direct(ref direct) => direct.seat(),
        }
    }
}

impl SessionNotifier for AutoSessionNotifier {
    type Id = AutoId;

    fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
        &mut self, signal: &mut A
    ) -> Self::Id {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &mut AutoSessionNotifier::Logind(ref mut logind) => {
                AutoId(AutoIdInternal::Logind(logind.register(signal)))
            }
            &mut AutoSessionNotifier::Direct(ref mut direct) => {
                AutoId(AutoIdInternal::Direct(direct.register(signal)))
            }
        }
    }
    fn unregister(&mut self, signal: Self::Id) {
        match (self, signal) {
            #[cfg(feature = "backend_session_logind")]
            (&mut AutoSessionNotifier::Logind(ref mut logind), AutoId(AutoIdInternal::Logind(signal))) => {
                logind.unregister(signal)
            }
            (&mut AutoSessionNotifier::Direct(ref mut direct), AutoId(AutoIdInternal::Direct(signal))) => {
                direct.unregister(signal)
            }
            _ => unreachable!(),
        }
    }

    fn is_active(&self) -> bool {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &AutoSessionNotifier::Logind(ref logind) => logind.is_active(),
            &AutoSessionNotifier::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> &str {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &AutoSessionNotifier::Logind(ref logind) => logind.seat(),
            &AutoSessionNotifier::Direct(ref direct) => direct.seat(),
        }
    }
}

impl BoundAutoSession {
    /// Unbind the session from the `EventLoop` again
    pub fn unbind(self) -> AutoSessionNotifier {
        match self {
            #[cfg(feature = "backend_session_logind")]
            BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
            BoundAutoSession::Direct(source) => AutoSessionNotifier::Direct(source.remove()),
        }
    }
}

error_chain! {
    links {
        Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")] #[doc = "Underlying logind session error"];
    }

    foreign_links {
        Direct(::nix::Error) #[doc = "Underlying direct tty session error"];
    }
}

impl AsErrno for Error {
    fn as_errno(&self) -> Option<i32> {
        //TODO figure this out, I don't see a way..
        None
    }
}
<commit_msg>Add missing derives for AutoId<commit_after>//!
//! Implementation of the `Session` trait through various implementations
//! automatically choosing the best available interface.
//!
//! ## How to use it
//!
//! ### Initialization
//!
//! To initialize a session just call `AutoSession::new`. A new session will be opened, if the
//! any available interface is successful and will be closed once the `AutoSessionNotifier` is dropped.
//!
//! ### Usage of the session
//!
//! The session may be used to open devices manually through the `Session` interface
//! or be passed to other objects that need it to open devices themselves.
//! The `AutoSession` is clonable and may be passed to multiple devices easily.
//!
//! Examples for those are e.g. the `LibinputInputBackend` (its context might be initialized through a
//! `Session` via the `LibinputSessionInterface`) or the `UdevBackend`.
//!
//! ### Usage of the session notifier
//!
//! The notifier might be used to pause device access, when the session gets paused (e.g. by
//! switching the tty via `AutoSession::change_vt`) and to automatically enable it again,
//! when the session becomes active again.
//!
//! It is crutial to avoid errors during that state. Examples for object that might be registered
//! for notifications are the `Libinput` context, the `UdevBackend` or a `DrmDevice` (handled
//! automatically by the `UdevBackend`, if not done manually).
//! ```

use super::{AsErrno, AsSessionObserver, Session, SessionNotifier, SessionObserver};
use super::direct::{self, direct_session_bind, DirectSession, DirectSessionNotifier};
#[cfg(feature = "backend_session_logind")]
use super::logind::{self, logind_session_bind, BoundLogindSession, LogindSession, LogindSessionNotifier};
use nix::fcntl::OFlag;
use std::cell::RefCell;
use std::io::Error as IoError;
use std::os::unix::io::RawFd;
use std::path::Path;
use std::rc::Rc;
use wayland_server::EventLoopHandle;
use wayland_server::sources::{EventSource, SignalEventSource};

/// `Session` using the best available inteface
#[derive(Clone)]
pub enum AutoSession {
    /// Logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSession),
    /// Direct / tty session
    Direct(Rc<RefCell<DirectSession>>),
}

/// `SessionNotifier` using the best available inteface
pub enum AutoSessionNotifier {
    /// Logind session nofifier
    #[cfg(feature = "backend_session_logind")]
    Logind(LogindSessionNotifier),
    /// Direct / tty session notifier
    Direct(DirectSessionNotifier),
}

/// Bound session that is driven by the `wayland_server::EventLoop`.
///
/// See `auto_session_bind` for details.
///
/// Dropping this object will close the session just like the `AutoSessionNotifier`.
pub enum BoundAutoSession {
    /// Bound logind session
    #[cfg(feature = "backend_session_logind")]
    Logind(BoundLogindSession),
    /// Bound direct / tty session
    Direct(SignalEventSource<DirectSessionNotifier>),
}

/// Id's used by the `AutoSessionNotifier` internally.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct AutoId(AutoIdInternal);
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum AutoIdInternal {
    #[cfg(feature = "backend_session_logind")]
    Logind(logind::Id),
    Direct(direct::Id),
}

impl AutoSession {
    /// Tries to create a new session via the best available interface.
    #[cfg(feature = "backend_session_logind")]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = ::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create logind session");
        match LogindSession::new(logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Logind(session),
                AutoSessionNotifier::Logind(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create logind session: {}", err);
                info!(logger, "Falling back to create tty session");
                match DirectSession::new(None, logger.clone()) {
                    Ok((session, notifier)) => Some((
                        AutoSession::Direct(Rc::new(RefCell::new(session))),
                        AutoSessionNotifier::Direct(notifier),
                    )),
                    Err(err) => {
                        warn!(logger, "Failed to create direct session: {}", err);
                        error!(
                            logger,
                            "Could not create any session, possibilities exhausted"
                        );
                        None
                    }
                }
            }
        }
    }

    #[cfg(not(feature = "backend_session_logind"))]
    pub fn new<L>(logger: L) -> Option<(AutoSession, AutoSessionNotifier)>
    where
        L: Into<Option<::slog::Logger>>,
    {
        let logger = ::slog_or_stdlog(logger)
            .new(o!("smithay_module" => "backend_session_auto", "session_type" => "auto"));

        info!(logger, "Trying to create tty session");
        match DirectSession::new(None, logger.clone()) {
            Ok((session, notifier)) => Some((
                AutoSession::Direct(Rc::new(RefCell::new(session))),
                AutoSessionNotifier::Direct(notifier),
            )),
            Err(err) => {
                warn!(logger, "Failed to create direct session: {}", err);
                error!(
                    logger,
                    "Could not create any session, possibilities exhausted"
                );
                None
            }
        }
    }
}

/// Bind an `AutoSessionNotifier` to an `EventLoop`.
///
/// Allows the `AutoSessionNotifier` to listen for incoming signals signalling the session state.
/// If you don't use this function `AutoSessionNotifier` will not correctly tell you the
/// session state and call it's `SessionObservers`.
pub fn auto_session_bind(
    notifier: AutoSessionNotifier, evlh: &mut EventLoopHandle
) -> ::std::result::Result<BoundAutoSession, (IoError, AutoSessionNotifier)> {
    Ok(match notifier {
        #[cfg(feature = "backend_session_logind")]
        AutoSessionNotifier::Logind(logind) => BoundAutoSession::Logind(logind_session_bind(logind, evlh)
            .map_err(|(error, notifier)| (error, AutoSessionNotifier::Logind(notifier)))?),
        AutoSessionNotifier::Direct(direct) => BoundAutoSession::Direct(direct_session_bind(direct, evlh)
            .map_err(|(error, notifier)| (error, AutoSessionNotifier::Direct(notifier)))?),
    })
}

impl Session for AutoSession {
    type Error = Error;

    fn open(&mut self, path: &Path, flags: OFlag) -> Result<RawFd> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &mut AutoSession::Logind(ref mut logind) => logind.open(path, flags).map_err(|e| e.into()),
            &mut AutoSession::Direct(ref mut direct) => direct.open(path, flags).map_err(|e| e.into()),
        }
    }
    fn close(&mut self, fd: RawFd) -> Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &mut AutoSession::Logind(ref mut logind) => logind.close(fd).map_err(|e| e.into()),
            &mut AutoSession::Direct(ref mut direct) => direct.close(fd).map_err(|e| e.into()),
        }
    }

    fn change_vt(&mut self, vt: i32) -> Result<()> {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &mut AutoSession::Logind(ref mut logind) => logind.change_vt(vt).map_err(|e| e.into()),
            &mut AutoSession::Direct(ref mut direct) => direct.change_vt(vt).map_err(|e| e.into()),
        }
    }

    fn is_active(&self) -> bool {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &AutoSession::Logind(ref logind) => logind.is_active(),
            &AutoSession::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> String {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &AutoSession::Logind(ref logind) => logind.seat(),
            &AutoSession::Direct(ref direct) => direct.seat(),
        }
    }
}

impl SessionNotifier for AutoSessionNotifier {
    type Id = AutoId;

    fn register<S: SessionObserver + 'static, A: AsSessionObserver<S>>(
        &mut self, signal: &mut A
    ) -> Self::Id {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &mut AutoSessionNotifier::Logind(ref mut logind) => {
                AutoId(AutoIdInternal::Logind(logind.register(signal)))
            }
            &mut AutoSessionNotifier::Direct(ref mut direct) => {
                AutoId(AutoIdInternal::Direct(direct.register(signal)))
            }
        }
    }
    fn unregister(&mut self, signal: Self::Id) {
        match (self, signal) {
            #[cfg(feature = "backend_session_logind")]
            (&mut AutoSessionNotifier::Logind(ref mut logind), AutoId(AutoIdInternal::Logind(signal))) => {
                logind.unregister(signal)
            }
            (&mut AutoSessionNotifier::Direct(ref mut direct), AutoId(AutoIdInternal::Direct(signal))) => {
                direct.unregister(signal)
            }
            _ => unreachable!(),
        }
    }

    fn is_active(&self) -> bool {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &AutoSessionNotifier::Logind(ref logind) => logind.is_active(),
            &AutoSessionNotifier::Direct(ref direct) => direct.is_active(),
        }
    }
    fn seat(&self) -> &str {
        match self {
            #[cfg(feature = "backend_session_logind")]
            &AutoSessionNotifier::Logind(ref logind) => logind.seat(),
            &AutoSessionNotifier::Direct(ref direct) => direct.seat(),
        }
    }
}

impl BoundAutoSession {
    /// Unbind the session from the `EventLoop` again
    pub fn unbind(self) -> AutoSessionNotifier {
        match self {
            #[cfg(feature = "backend_session_logind")]
            BoundAutoSession::Logind(logind) => AutoSessionNotifier::Logind(logind.unbind()),
            BoundAutoSession::Direct(source) => AutoSessionNotifier::Direct(source.remove()),
        }
    }
}

error_chain! {
    links {
        Logind(logind::Error, logind::ErrorKind) #[cfg(feature = "backend_session_logind")] #[doc = "Underlying logind session error"];
    }

    foreign_links {
        Direct(::nix::Error) #[doc = "Underlying direct tty session error"];
    }
}

impl AsErrno for Error {
    fn as_errno(&self) -> Option<i32> {
        //TODO figure this out, I don't see a way..
        None
    }
}
<|endoftext|>


<commit_before>#![feature(alloc)]
#![feature(oom)]

#![allow(unused_features)]
#![allow(unused_imports)]
#![allow(dead_code)]
#![allow(unused_variables)]

pub mod ffi;
pub mod boring;
pub mod parsing;

pub use ::ffi::{IoDirection, IoMode};

#[macro_use]
extern crate json;
extern crate libc;
extern crate alloc;
use std::marker;
use std::ptr;
use std::cell::RefCell;

struct ContextPtr {
    ptr: Option<*mut ::ffi::Context>
}
pub struct Context{
    p: RefCell<ContextPtr>
}

struct JobPtr{
    ptr: Option<*mut ::ffi::Job>
}

pub struct Job{
    p: RefCell<JobPtr>
}

struct JobIoPtr{
    ptr: Option<*mut ::ffi::JobIO>
}

pub struct JobIo<'a, T: 'a>{
    p: RefCell<JobIoPtr>,
    _marker: marker::PhantomData<&'a T>
}

#[derive(Debug, PartialEq)]
pub struct FlowErr{
    code: i32,
    message_and_stack: String
}

#[derive(Debug, PartialEq)]
pub enum FlowError {
    ContextInvalid,
    Oom,
    Err(FlowErr),
    ErrNotImpl

}

pub type Result<T> = std::result::Result<T, FlowError>;

impl ContextPtr {
    fn destroy(&mut self){
        unsafe {
            self.ptr = match self.ptr{
                Some(ptr) => {
                    ::ffi::flow_context_destroy(ptr);
                    None
                }
                _ => None
            }
        }
    }

    unsafe fn get_flow_err(&self, c: *mut ::ffi::Context) -> FlowErr{

        let code = ::ffi::flow_context_error_reason(c);
        let mut buf = vec![0u8; 2048];

        let chars_written = ::ffi::flow_context_error_and_stacktrace(c, buf.as_mut_ptr(), buf.len(),false);

        if chars_written < 0 {
            panic!("Error msg doesn't fit in 2kb");
        }else {
            buf.resize(chars_written as usize, 0u8);
        }

        FlowErr {
            code: code,
            message_and_stack: String::from_utf8(buf).unwrap()
        }

    }

    fn get_error_copy(&self) -> Option<FlowError> {
        unsafe {
            match self.ptr {
                Some(ptr) if ::ffi::flow_context_has_error(ptr) => {
                    match ::ffi::flow_context_error_reason(ptr){
                        0 => panic!("Inconsistent errors"),
                        10 => Some(FlowError::Oom),
                        _ => Some(FlowError::Err(self.get_flow_err(ptr)))
                    }
                },
                None => Some(FlowError::ContextInvalid),
                Some(_) => None
            }
        }
    }
}
impl Drop for Context {
    fn drop(&mut self) {
        (*self.p.borrow_mut()).destroy();
    }
}
impl Context {
    pub fn create() -> Context {
        unsafe {
            let ptr = ::ffi::flow_context_create();

            if ptr.is_null() {
                Context {
                    p: RefCell::new(ContextPtr { ptr: None }),
                }
            } else {
                Context {
                    p: RefCell::new(ContextPtr { ptr: Some(ptr) }),
                }
            }
        }
    }

    fn get_error_copy(&self) -> Option<FlowError> {
        (*self.p.borrow()).get_error_copy()
    }

    pub fn destroy(self) -> Result<()> {
        let ref mut b = *self.p.borrow_mut();
        match b.ptr {
            None => Ok(()),
            Some(ptr) => unsafe {
                if !::ffi::flow_context_begin_terminate(ptr) {
                    //Already borrowed; will panic!
                    //This kind of bug is only exposed at runtime, now.
                    //Code reuse will require two copies of every function
                    //One against the ContextPtr, to be reused
                    //One exposed publicly against the Context, which performs the borrowing
                    //Same scenario will occur with other types.
                    //let copy = self.get_error_copy().unwrap();

                    //So use the ContextPtr version
                    let copy = b.get_error_copy().unwrap();
                    b.destroy();
                    Err(copy)
                } else {
                    b.destroy();
                    Ok(())
                }
            }
        }
    }

    pub fn create_job(&mut self) -> Result<Job> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {
                let p = ::ffi::flow_job_create(ptr);
                if p.is_null() {
                    Err(b.get_error_copy().unwrap())
                } else {
                    Ok(Job { p: RefCell::new(JobPtr { ptr: Some(p) }) })
                }
            }
        }
    }

    pub fn create_io_from_slice<'a, 'c>(&'c mut self, bytes: &'a [u8]) -> Result<JobIo<'a, &'a [u8]>> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {
                let p = ::ffi::flow_io_create_from_memory(ptr, ::ffi::IoMode::read_seekable, bytes.as_ptr(), bytes.len(), ptr as *const libc::c_void, ptr::null());
                if p.is_null() {
                    Err(b.get_error_copy().unwrap())
                } else {
                    Ok(JobIo{ _marker: marker::PhantomData, p: RefCell::new(JobIoPtr { ptr: Some(p) }) })
                }
            }
        }
    }

    pub fn create_io_output_buffer<'a, 'b>(&'a mut self) -> Result<JobIo<'b,()>> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {
                let p = ::ffi::flow_io_create_for_output_buffer(ptr, ptr as *const libc::c_void);
                if p.is_null() {
                    Err(b.get_error_copy().unwrap())
                } else {
                    Ok(JobIo{ _marker: marker::PhantomData, p: RefCell::new(JobIoPtr { ptr: Some(p) }) })
                }
            }
        }
    }

    pub fn job_add_io<T>(&mut self, job: &mut Job, io: JobIo<T>, io_id: i32, direction: IoDirection) -> Result<()> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {
                let p = ::ffi::flow_job_add_io(ptr, (*job.p.borrow_mut()).ptr.unwrap(),  (*io.p.borrow_mut()).ptr.unwrap(), io_id, direction);
                if !p {
                    Err(b.get_error_copy().unwrap())
                } else {
                    Ok(())
                }
            }
        }
    }

    pub fn io_get_output_buffer<'a, 'b>(&'a mut self, job: &'b Job, io_id: i32) -> Result<&'b [u8]> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {

                let io_p = ::ffi::flow_job_get_io(ptr,  (*job.p.borrow_mut()).ptr.unwrap(), io_id);
                if io_p.is_null() {
                    Err(b.get_error_copy().unwrap())
                } else {
                    let mut buf_start: *const u8 = ptr::null();
                    let mut buf_len: usize = 0;
                    let worked = ::ffi::flow_io_get_output_buffer(ptr, io_p, &mut buf_start as *mut *const u8, &mut buf_len as *mut usize);
                    if !worked{
                        Err(b.get_error_copy().unwrap())
                    } else {
                        if buf_start.is_null(){
                            Err(FlowError::ErrNotImpl) //Not sure how output buffer is null... no writes yet?
                        }else {
                            Ok((std::slice::from_raw_parts(buf_start, buf_len)))
                        }
                    }
                }

            }
        }
    }

}

#[test]
fn it_works() {
    let mut c = Context::create();

    let mut j = c.create_job().unwrap();

    let bytes = [ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00,
    0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00,
    0x00, 0x00, 0x0A, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01,
    0x0D, 0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 ];

    let input = c.create_io_from_slice(&bytes).unwrap();

    let output = c.create_io_output_buffer().unwrap();

    c.job_add_io(&mut j, input, 0, IoDirection::In).unwrap();
    c.job_add_io(&mut j, output, 1, IoDirection::Out).unwrap();

    //let output_bytes = c.io_get_output_buffer(&j, 1).unwrap();

    assert_eq!(c.destroy(), Ok(()));

}

//pub struct FlowIoRef{
//    ptr: *mut ::ffi::JobIO
//}

pub struct JsonResponse<'a>{
    pub status_code: i64,
    pub response_json: &'a [u8]
}

impl Context{
    pub fn message(&mut self, method: &str, json: &[u8]) -> JsonResponse{

        match method {
            "teapot" => JsonResponse {
                status_code: 418,
                response_json:
                r#"{"success": "false","code": 418,"message": "I'm a teapot, short and stout"}"#
                    .as_bytes()
            },
            _ => JsonResponse {
                status_code: 404,
                response_json: r#"{
                                        "success": "false",
                                        "code": 404,
                                        "message": "Method not understood"}"#.as_bytes()
            }
        }
    }

    fn build_graph(&mut self, json: &[u8]){

    }

}

impl Job{

    pub fn message(&self, context: &mut Context, method: &str, json: &[u8]) -> JsonResponse{

        match method {
            "execute" => {

                //build graph
                //execute graph
                JsonResponse {
                    status_code: 200,
                    response_json:
                    r#"{"success": "false","code": 418,"message": "I'm a teapot, short and stout"}"#
                        .as_bytes()
                }
            },
            _ => JsonResponse {
                status_code: 404,
                response_json: r#"{
                                        "success": "false",
                                        "code": 404,
                                        "message": "Method not understood"}"#.as_bytes()
            }
        }
    }
}

//
//impl SpeakJson for FlowContext{
//    fn message(&self, method: &str, json: &str) -> &JsonResponse{
//
//    }
//}

//
//pub struct FlowCtx{
//    ptr: *mut ::ffi::Context,
//
//}
//impl FlowCtx {
//    pub fn from_ptr(ptr: *mut ::ffi::Context) -> FlowCtx{
//        FlowCtx{ptr: ptr}
//    }
//}
//
//
//
//struct FlowIo{
//
//}

//#[test]
//fn test_panics(){
//    let result = ::std::panic::catch_unwind(|| {
//        panic!("oh no!");
//    });
//
//    if let Err(err) = result {
//        let str = format!("{:?}", err.downcast::<&'static str>());
//        assert_eq!(str, "");
//    }
//}

#[test]
fn test_panics2(){
   // let input_bytes = [0u8;3000000];
//    let result = ::std::panic::catch_unwind(|| {
//        let input_bytes = [2u8;10 * 1024 * 1024 * 1024];
//    });

//    if let Err(err) = result {
//        let str = format!("{:?}", err.downcast::<&'static str>());
//        assert_eq!(str, "");
//    }
}

fn new_oom_handler() -> ! {
    panic!("OOM");
}

#[allow(unused_variables)]
#[test]
fn test_panics3(){

    alloc::oom::set_oom_handler(new_oom_handler);

    // let input_bytes = [0u8;3000000];
    let result = ::std::panic::catch_unwind(|| {
        let b = vec![0;30 * 1024 * 1024 * 1024];
    });

    if let Err(err) = result {
        let str = format!("{:?}", err.downcast::<&'static str>());
        assert_eq!(str, "Ok(\"OOM\")");
    }
}<commit_msg>Don't make OOM testing happen on CI<commit_after>#![feature(alloc)]
#![feature(oom)]

#![allow(unused_features)]
#![allow(unused_imports)]
#![allow(dead_code)]
#![allow(unused_variables)]

pub mod ffi;
pub mod boring;
pub mod parsing;

pub use ::ffi::{IoDirection, IoMode};

#[macro_use]
extern crate json;
extern crate libc;
extern crate alloc;
use std::marker;
use std::ptr;
use std::cell::RefCell;

struct ContextPtr {
    ptr: Option<*mut ::ffi::Context>
}
pub struct Context{
    p: RefCell<ContextPtr>
}

struct JobPtr{
    ptr: Option<*mut ::ffi::Job>
}

pub struct Job{
    p: RefCell<JobPtr>
}

struct JobIoPtr{
    ptr: Option<*mut ::ffi::JobIO>
}

pub struct JobIo<'a, T: 'a>{
    p: RefCell<JobIoPtr>,
    _marker: marker::PhantomData<&'a T>
}

#[derive(Debug, PartialEq)]
pub struct FlowErr{
    code: i32,
    message_and_stack: String
}

#[derive(Debug, PartialEq)]
pub enum FlowError {
    ContextInvalid,
    Oom,
    Err(FlowErr),
    ErrNotImpl

}

pub type Result<T> = std::result::Result<T, FlowError>;

impl ContextPtr {
    fn destroy(&mut self){
        unsafe {
            self.ptr = match self.ptr{
                Some(ptr) => {
                    ::ffi::flow_context_destroy(ptr);
                    None
                }
                _ => None
            }
        }
    }

    unsafe fn get_flow_err(&self, c: *mut ::ffi::Context) -> FlowErr{

        let code = ::ffi::flow_context_error_reason(c);
        let mut buf = vec![0u8; 2048];

        let chars_written = ::ffi::flow_context_error_and_stacktrace(c, buf.as_mut_ptr(), buf.len(),false);

        if chars_written < 0 {
            panic!("Error msg doesn't fit in 2kb");
        }else {
            buf.resize(chars_written as usize, 0u8);
        }

        FlowErr {
            code: code,
            message_and_stack: String::from_utf8(buf).unwrap()
        }

    }

    fn get_error_copy(&self) -> Option<FlowError> {
        unsafe {
            match self.ptr {
                Some(ptr) if ::ffi::flow_context_has_error(ptr) => {
                    match ::ffi::flow_context_error_reason(ptr){
                        0 => panic!("Inconsistent errors"),
                        10 => Some(FlowError::Oom),
                        _ => Some(FlowError::Err(self.get_flow_err(ptr)))
                    }
                },
                None => Some(FlowError::ContextInvalid),
                Some(_) => None
            }
        }
    }
}
impl Drop for Context {
    fn drop(&mut self) {
        (*self.p.borrow_mut()).destroy();
    }
}
impl Context {
    pub fn create() -> Context {
        unsafe {
            let ptr = ::ffi::flow_context_create();

            if ptr.is_null() {
                Context {
                    p: RefCell::new(ContextPtr { ptr: None }),
                }
            } else {
                Context {
                    p: RefCell::new(ContextPtr { ptr: Some(ptr) }),
                }
            }
        }
    }

    fn get_error_copy(&self) -> Option<FlowError> {
        (*self.p.borrow()).get_error_copy()
    }

    pub fn destroy(self) -> Result<()> {
        let ref mut b = *self.p.borrow_mut();
        match b.ptr {
            None => Ok(()),
            Some(ptr) => unsafe {
                if !::ffi::flow_context_begin_terminate(ptr) {
                    //Already borrowed; will panic!
                    //This kind of bug is only exposed at runtime, now.
                    //Code reuse will require two copies of every function
                    //One against the ContextPtr, to be reused
                    //One exposed publicly against the Context, which performs the borrowing
                    //Same scenario will occur with other types.
                    //let copy = self.get_error_copy().unwrap();

                    //So use the ContextPtr version
                    let copy = b.get_error_copy().unwrap();
                    b.destroy();
                    Err(copy)
                } else {
                    b.destroy();
                    Ok(())
                }
            }
        }
    }

    pub fn create_job(&mut self) -> Result<Job> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {
                let p = ::ffi::flow_job_create(ptr);
                if p.is_null() {
                    Err(b.get_error_copy().unwrap())
                } else {
                    Ok(Job { p: RefCell::new(JobPtr { ptr: Some(p) }) })
                }
            }
        }
    }

    pub fn create_io_from_slice<'a, 'c>(&'c mut self, bytes: &'a [u8]) -> Result<JobIo<'a, &'a [u8]>> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {
                let p = ::ffi::flow_io_create_from_memory(ptr, ::ffi::IoMode::read_seekable, bytes.as_ptr(), bytes.len(), ptr as *const libc::c_void, ptr::null());
                if p.is_null() {
                    Err(b.get_error_copy().unwrap())
                } else {
                    Ok(JobIo{ _marker: marker::PhantomData, p: RefCell::new(JobIoPtr { ptr: Some(p) }) })
                }
            }
        }
    }

    pub fn create_io_output_buffer<'a, 'b>(&'a mut self) -> Result<JobIo<'b,()>> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {
                let p = ::ffi::flow_io_create_for_output_buffer(ptr, ptr as *const libc::c_void);
                if p.is_null() {
                    Err(b.get_error_copy().unwrap())
                } else {
                    Ok(JobIo{ _marker: marker::PhantomData, p: RefCell::new(JobIoPtr { ptr: Some(p) }) })
                }
            }
        }
    }

    pub fn job_add_io<T>(&mut self, job: &mut Job, io: JobIo<T>, io_id: i32, direction: IoDirection) -> Result<()> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {
                let p = ::ffi::flow_job_add_io(ptr, (*job.p.borrow_mut()).ptr.unwrap(),  (*io.p.borrow_mut()).ptr.unwrap(), io_id, direction);
                if !p {
                    Err(b.get_error_copy().unwrap())
                } else {
                    Ok(())
                }
            }
        }
    }

    pub fn io_get_output_buffer<'a, 'b>(&'a mut self, job: &'b Job, io_id: i32) -> Result<&'b [u8]> {
        let ref b = *self.p.borrow_mut();
        match b.ptr {
            None => Err(FlowError::ContextInvalid),
            Some(ptr) => unsafe {

                let io_p = ::ffi::flow_job_get_io(ptr,  (*job.p.borrow_mut()).ptr.unwrap(), io_id);
                if io_p.is_null() {
                    Err(b.get_error_copy().unwrap())
                } else {
                    let mut buf_start: *const u8 = ptr::null();
                    let mut buf_len: usize = 0;
                    let worked = ::ffi::flow_io_get_output_buffer(ptr, io_p, &mut buf_start as *mut *const u8, &mut buf_len as *mut usize);
                    if !worked{
                        Err(b.get_error_copy().unwrap())
                    } else {
                        if buf_start.is_null(){
                            Err(FlowError::ErrNotImpl) //Not sure how output buffer is null... no writes yet?
                        }else {
                            Ok((std::slice::from_raw_parts(buf_start, buf_len)))
                        }
                    }
                }

            }
        }
    }

}

#[test]
fn it_works() {
    let mut c = Context::create();

    let mut j = c.create_job().unwrap();

    let bytes = [ 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00,
    0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x00, 0x00, 0x00, 0x1F, 0x15, 0xC4, 0x89, 0x00,
    0x00, 0x00, 0x0A, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9C, 0x63, 0x00, 0x01, 0x00, 0x00, 0x05, 0x00, 0x01,
    0x0D, 0x0A, 0x2D, 0xB4, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82 ];

    let input = c.create_io_from_slice(&bytes).unwrap();

    let output = c.create_io_output_buffer().unwrap();

    c.job_add_io(&mut j, input, 0, IoDirection::In).unwrap();
    c.job_add_io(&mut j, output, 1, IoDirection::Out).unwrap();

    //let output_bytes = c.io_get_output_buffer(&j, 1).unwrap();

    assert_eq!(c.destroy(), Ok(()));

}

//pub struct FlowIoRef{
//    ptr: *mut ::ffi::JobIO
//}

pub struct JsonResponse<'a>{
    pub status_code: i64,
    pub response_json: &'a [u8]
}

impl Context{
    pub fn message(&mut self, method: &str, json: &[u8]) -> JsonResponse{

        match method {
            "teapot" => JsonResponse {
                status_code: 418,
                response_json:
                r#"{"success": "false","code": 418,"message": "I'm a teapot, short and stout"}"#
                    .as_bytes()
            },
            _ => JsonResponse {
                status_code: 404,
                response_json: r#"{
                                        "success": "false",
                                        "code": 404,
                                        "message": "Method not understood"}"#.as_bytes()
            }
        }
    }

    fn build_graph(&mut self, json: &[u8]){

    }

}

impl Job{

    pub fn message(&self, context: &mut Context, method: &str, json: &[u8]) -> JsonResponse{

        match method {
            "execute" => {

                //build graph
                //execute graph
                JsonResponse {
                    status_code: 200,
                    response_json:
                    r#"{"success": "false","code": 418,"message": "I'm a teapot, short and stout"}"#
                        .as_bytes()
                }
            },
            _ => JsonResponse {
                status_code: 404,
                response_json: r#"{
                                        "success": "false",
                                        "code": 404,
                                        "message": "Method not understood"}"#.as_bytes()
            }
        }
    }
}

//
//impl SpeakJson for FlowContext{
//    fn message(&self, method: &str, json: &str) -> &JsonResponse{
//
//    }
//}

//
//pub struct FlowCtx{
//    ptr: *mut ::ffi::Context,
//
//}
//impl FlowCtx {
//    pub fn from_ptr(ptr: *mut ::ffi::Context) -> FlowCtx{
//        FlowCtx{ptr: ptr}
//    }
//}
//
//
//
//struct FlowIo{
//
//}

//#[test]
//fn test_panics(){
//    let result = ::std::panic::catch_unwind(|| {
//        panic!("oh no!");
//    });
//
//    if let Err(err) = result {
//        let str = format!("{:?}", err.downcast::<&'static str>());
//        assert_eq!(str, "");
//    }
//}

#[test]
fn test_panics2(){
   // let input_bytes = [0u8;3000000];
//    let result = ::std::panic::catch_unwind(|| {
//        let input_bytes = [2u8;10 * 1024 * 1024 * 1024];
//    });

//    if let Err(err) = result {
//        let str = format!("{:?}", err.downcast::<&'static str>());
//        assert_eq!(str, "");
//    }
}

//fn new_oom_handler() -> ! {
//    panic!("OOM");
//}
//
//#[allow(unused_variables)]
//#[test]
//fn test_panics3(){
//
//    alloc::oom::set_oom_handler(new_oom_handler);
//
//    // let input_bytes = [0u8;3000000];
//    let result = ::std::panic::catch_unwind(|| {
//        let b = vec![0;30 * 1024 * 1024 * 1024];
//    });
//
//    if let Err(err) = result {
//        let str = format!("{:?}", err.downcast::<&'static str>());
//        assert_eq!(str, "Ok(\"OOM\")");
//    }
//}<|endoftext|>


<commit_before>use model::prelude::*;
use parking_lot::RwLock;
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use super::context::Context;
use ::client::bridge::gateway::event::*;

pub trait EventHandler {
    #[cfg(feature = "cache")]
    fn cached(&self, _: Context, _: Vec<GuildId>) {}
    fn channel_create(&self, _: Context, _: Arc<RwLock<GuildChannel>>) {}
    fn category_create(&self, _: Context, _: Arc<RwLock<ChannelCategory>>) {}
    fn category_delete(&self, _: Context, _: Arc<RwLock<ChannelCategory>>) {}
    fn private_channel_create(&self, _: Context, _: Arc<RwLock<PrivateChannel>>) {}
    fn channel_delete(&self, _: Context, _: Arc<RwLock<GuildChannel>>) {}
    fn channel_pins_update(&self, _: Context, _: ChannelPinsUpdateEvent) {}
    fn channel_recipient_addition(&self, _: Context, _: ChannelId, _: User) {}
    fn channel_recipient_removal(&self, _: Context, _: ChannelId, _: User) {}
    #[cfg(feature = "cache")]
    fn channel_update(&self, _: Context, _: Option<Channel>, _: Channel) {}
    #[cfg(not(feature = "cache"))]
    fn channel_update(&self, _: Context, _: Channel) {}
    fn guild_ban_addition(&self, _: Context, _: GuildId, _: User) {}
    fn guild_ban_removal(&self, _: Context, _: GuildId, _: User) {}
    #[cfg(feature = "cache")]
    fn guild_create(&self, _: Context, _: Guild, _: bool) {}
    #[cfg(not(feature = "cache"))]
    fn guild_create(&self, _: Context, _: Guild) {}
    #[cfg(feature = "cache")]
    fn guild_delete(&self, _: Context, _: PartialGuild, _: Option<Arc<RwLock<Guild>>>) {}
    #[cfg(not(feature = "cache"))]
    fn guild_delete(&self, _: Context, _: PartialGuild) {}
    fn guild_emojis_update(&self, _: Context, _: GuildId, _: HashMap<EmojiId, Emoji>) {}
    fn guild_integrations_update(&self, _: Context, _: GuildId) {}
    fn guild_member_addition(&self, _: Context, _: GuildId, _: Member) {}
    #[cfg(feature = "cache")]
    fn guild_member_removal(&self, _: Context, _: GuildId, _: User, _: Option<Member>) {}
    #[cfg(not(feature = "cache"))]
    fn guild_member_removal(&self, _: Context, _: GuildId, _: User) {}
    #[cfg(feature = "cache")]
    fn guild_member_update(&self, _: Context, _: Option<Member>, _: Member) {}
    #[cfg(not(feature = "cache"))]
    fn guild_member_update(&self, _: Context, _: GuildMemberUpdateEvent) {}
    fn guild_members_chunk(&self, _: Context, _: GuildId, _: HashMap<UserId, Member>) {}
    fn guild_role_create(&self, _: Context, _: GuildId, _: Role) {}
    #[cfg(feature = "cache")]
    fn guild_role_delete(&self, _: Context, _: GuildId, _: RoleId, _: Option<Role>) {}
    #[cfg(not(feature = "cache"))]
    fn guild_role_delete(&self, _: Context, _: GuildId, _: RoleId) {}
    #[cfg(feature = "cache")]
    fn guild_role_update(&self, _: Context, _: GuildId, _: Option<Role>, _: Role) {}
    #[cfg(not(feature = "cache"))]
    fn guild_role_update(&self, _: Context, _: GuildId, _: Role) {}
    fn guild_unavailable(&self, _: Context, _: GuildId) {}
    #[cfg(feature = "cache")]
    fn guild_update(&self, _: Context, _: Option<Arc<RwLock<Guild>>>, _: PartialGuild) {}
    #[cfg(not(feature = "cache"))]
    fn guild_update(&self, _: Context, _: PartialGuild) {}
    fn message(&self, _: Context, _: Message) {}
    fn message_delete(&self, _: Context, _: ChannelId, _: MessageId) {}
    fn message_delete_bulk(&self, _: Context, _: ChannelId, _: Vec<MessageId>) {}
    fn reaction_add(&self, _: Context, _: Reaction) {}
    fn reaction_remove(&self, _: Context, _: Reaction) {}
    fn reaction_remove_all(&self, _: Context, _: ChannelId, _: MessageId) {}
    fn message_update(&self, _: Context, _: MessageUpdateEvent) {}
    fn presence_replace(&self, _: Context, _: Vec<Presence>) {}
    fn presence_update(&self, _: Context, _: PresenceUpdateEvent) {}
    fn ready(&self, _: Context, _: Ready) {}
    fn resume(&self, _: Context, _: ResumedEvent) {}

    /// Called when a shard's connection stage is updated, providing the context
    /// of the shard and the event information about the update.
    fn shard_stage_update(&self, _: Context, _: ShardStageUpdateEvent) {}

    fn typing_start(&self, _: Context, _: TypingStartEvent) {}
    fn unknown(&self, _: Context, _: String, _: Value) {}
    #[cfg(feature = "cache")]
    fn user_update(&self, _: Context, _: CurrentUser, _: CurrentUser) {}
    #[cfg(not(feature = "cache"))]
    fn user_update(&self, _: Context, _: CurrentUser) {}
    fn voice_server_update(&self, _: Context, _: VoiceServerUpdateEvent) {}
    fn voice_state_update(&self, _: Context, _: Option<GuildId>, _: VoiceState) {}
    fn webhook_update(&self, _: Context, _: GuildId, _: ChannelId) {}
}
<commit_msg>Add documentation to `EventHandler`<commit_after>use model::prelude::*;
use parking_lot::RwLock;
use serde_json::Value;
use std::collections::HashMap;
use std::sync::Arc;
use super::context::Context;
use ::client::bridge::gateway::event::*;

/// The core trait for handling events by serenity.
pub trait EventHandler {
    /// Dispatched when the cache gets full.
    ///
    /// Provides the cached guilds' ids.
    #[cfg(feature = "cache")]
    fn cached(&self, _: Context, _: Vec<GuildId>) {}

    /// Dispatched when a channel is created.
    ///
    /// Provides said channel's data.
    fn channel_create(&self, _: Context, _: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a category is created.
    ///
    /// Provides said category's data.
    fn category_create(&self, _: Context, _: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a category is deleted.
    ///
    /// Provides said category's data.
    fn category_delete(&self, _: Context, _: Arc<RwLock<ChannelCategory>>) {}

    /// Dispatched when a private channel is created.
    ///
    /// Provides said channel's data.
    fn private_channel_create(&self, _: Context, _: Arc<RwLock<PrivateChannel>>) {}

    /// Dispatched when a channel is deleted.
    ///
    /// Provides said channel's data.
    fn channel_delete(&self, _: Context, _: Arc<RwLock<GuildChannel>>) {}

    /// Dispatched when a pin is added, deleted.
    ///
    /// Provides said pin's data.
    fn channel_pins_update(&self, _: Context, _: ChannelPinsUpdateEvent) {}

    /// Dispatched when a user is added to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_addition(&self, _: Context, _: ChannelId, _: User) {}

    /// Dispatched when a user is removed to a `Group`.
    ///
    /// Provides the group's id and the user's data.
    fn channel_recipient_removal(&self, _: Context, _: ChannelId, _: User) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the old channel data, and the new data.
    #[cfg(feature = "cache")]
    fn channel_update(&self, _: Context, _: Option<Channel>, _: Channel) {}

    /// Dispatched when a channel is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn channel_update(&self, _: Context, _: Channel) {}

    /// Dispatched when a user is banned from a guild.
    ///
    /// Provides the guild's id and the banned user's data.
    fn guild_ban_addition(&self, _: Context, _: GuildId, _: User) {}

    /// Dispatched when a user's ban is lifted from a guild.
    ///
    /// /// Provides the guild's id and the lifted user's data.
    fn guild_ban_removal(&self, _: Context, _: GuildId, _: User) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data and whether the guild is new.
    #[cfg(feature = "cache")]
    fn guild_create(&self, _: Context, _: Guild, _: bool) {}

    /// Dispatched when a guild is created;
    /// or an existing guild's data is sent to us.
    ///
    /// Provides the guild's data.
    #[cfg(not(feature = "cache"))]
    fn guild_create(&self, _: Context, _: Guild) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord,
    /// and the full data from the cache, if available.
    #[cfg(feature = "cache")]
    fn guild_delete(&self, _: Context, _: PartialGuild, _: Option<Arc<RwLock<Guild>>>) {}

    /// Dispatched when a guild is deleted.
    ///
    /// Provides the partial data of the guild sent by discord.
    #[cfg(not(feature = "cache"))]
    fn guild_delete(&self, _: Context, _: PartialGuild) {}

    /* the emojis were updated. */

    /// Dispatched when the emojis are updated.
    ///
    /// Provides the guild's id and the new state of the emojis in the guild.
    fn guild_emojis_update(&self, _: Context, _: GuildId, _: HashMap<EmojiId, Emoji>) {}

    /// Dispatched when a guild's integration is added, updated or removed.
    ///
    /// Provides the guild's id.
    fn guild_integrations_update(&self, _: Context, _: GuildId) {}

    /// Dispatched when a user joins a guild.
    ///
    /// Provides the guild's id and the user's member data.
    fn guild_member_addition(&self, _: Context, _: GuildId, _: Member) {}

    /// Dispatched when a user is removed (kicked).
    ///
    /// Provides the guild's id, the user's data, and the user's member data if available.
    #[cfg(feature = "cache")]
    fn guild_member_removal(&self, _: Context, _: GuildId, _: User, _: Option<Member>) {}

    /// Dispatched when a user is removed (kicked).
    ///
    /// Provides the guild's id, the user's data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_removal(&self, _: Context, _: GuildId, _: User) {}

    /// Dispatched when a member is updated (e.g their nickname is updated)
    ///
    /// Provides the member's old data (if available) and the new data.
    #[cfg(feature = "cache")]
    fn guild_member_update(&self, _: Context, _: Option<Member>, _: Member) {}

    /// Dispatched when a member is updated (e.g their nickname is updated)
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn guild_member_update(&self, _: Context, _: GuildMemberUpdateEvent) {}

    /// Dispatched when the data for offline members was requested.
    ///
    /// Provides the guild's id and the data.
    fn guild_members_chunk(&self, _: Context, _: GuildId, _: HashMap<UserId, Member>) {}

    /// Dispatched when a role is created.
    ///
    /// Provides the guild's id and the new role's data.
    fn guild_role_create(&self, _: Context, _: GuildId, _: Role) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id and its data if available.
    #[cfg(feature = "cache")]
    fn guild_role_delete(&self, _: Context, _: GuildId, _: RoleId, _: Option<Role>) {}

    /// Dispatched when a role is deleted.
    ///
    /// Provides the guild's id, the role's id.
    #[cfg(not(feature = "cache"))]
    fn guild_role_delete(&self, _: Context, _: GuildId, _: RoleId) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id, the role's old (if available) and new data.
    #[cfg(feature = "cache")]
    fn guild_role_update(&self, _: Context, _: GuildId, _: Option<Role>, _: Role) {}

    /// Dispatched when a role is updated.
    ///
    /// Provides the guild's id and the role's new data.
    #[cfg(not(feature = "cache"))]
    fn guild_role_update(&self, _: Context, _: GuildId, _: Role) {}

    /// Dispatched when a guild became unavailable.
    ///
    /// Provides the guild's id.
    fn guild_unavailable(&self, _: Context, _: GuildId) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's old full data (if available) and the new, albeit partial data.
    #[cfg(feature = "cache")]
    fn guild_update(&self, _: Context, _: Option<Arc<RwLock<Guild>>>, _: PartialGuild) {}

    /// Dispatched when the guild is updated.
    ///
    /// Provides the guild's new, albeit partial data.
    #[cfg(not(feature = "cache"))]
    fn guild_update(&self, _: Context, _: PartialGuild) {}

    /// Dispatched when a message is created.
    ///
    /// Provides the message's data.
    fn message(&self, _: Context, _: Message) {}

    /// Dispatched when a message is deleted.
    ///
    /// Provides the channel's id and the message's id.
    fn message_delete(&self, _: Context, _: ChannelId, _: MessageId) {}

    /// Dispatched when multiple messages were deleted at once.
    ///
    /// Provides the channel's id and the deleted messages' ids.
    fn message_delete_bulk(&self, _: Context, _: ChannelId, _: Vec<MessageId>) {}

    /// Dispatched when a new reaction is attached to a message.
    ///
    /// Provides the reaction's data.
    fn reaction_add(&self, _: Context, _: Reaction) {}

    /// Dispatched when a reaction is dettached from a message.
    ///
    /// Provides the reaction's data.
    fn reaction_remove(&self, _: Context, _: Reaction) {}

    /// Dispatched when all reactions of a message are dettached from a message.
    ///
    /// Provides the channel's id and the message's id.
    fn reaction_remove_all(&self, _: Context, _: ChannelId, _: MessageId) {}

    /// Dispatched when a message is updated.
    ///
    /// Provides the new data of the message.
    fn message_update(&self, _: Context, _: MessageUpdateEvent) {}

    fn presence_replace(&self, _: Context, _: Vec<Presence>) {}

    /// Dispatched when a user's presence is updated (i.e off -> on).
    ///
    /// Provides the presence's new data.
    fn presence_update(&self, _: Context, _: PresenceUpdateEvent) {}

    /// Dispatched upon startup.
    ///
    /// Provides data about the bot and the guilds it's in.
    fn ready(&self, _: Context, _: Ready) {}

    /// Dispatched upon reconnection.
    fn resume(&self, _: Context, _: ResumedEvent) {}

    /// Dispatched when a shard's connection stage is updated
    ///
    /// Provides the context of the shard and the event information about the update.
    fn shard_stage_update(&self, _: Context, _: ShardStageUpdateEvent) {}

    /// Dispatched when a user starts typing.
    fn typing_start(&self, _: Context, _: TypingStartEvent) {}

    /// Dispatched when an unknown event was sent from discord.
    ///
    /// Provides the event's name and its unparsed data.
    fn unknown(&self, _: Context, _: String, _: Value) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the old and new data.
    #[cfg(feature = "cache")]
    fn user_update(&self, _: Context, _: CurrentUser, _: CurrentUser) {}

    /// Dispatched when the bot's data is updated.
    ///
    /// Provides the new data.
    #[cfg(not(feature = "cache"))]
    fn user_update(&self, _: Context, _: CurrentUser) {}

    /// Dispatched when a guild's voice server was updated (or changed to another one).
    ///
    /// Provides the voice server's data.
    fn voice_server_update(&self, _: Context, _: VoiceServerUpdateEvent) {}

    /// Dispatched when a user joins, leaves or moves a voice channel.
    ///
    /// Provides the guild's id (if available) and
    /// the new state of the guild's voice channels.
    fn voice_state_update(&self, _: Context, _: Option<GuildId>, _: VoiceState) {}

    /// Dispatched when a guild's webhook is updated.
    ///
    /// Provides the guild's id and the channel's id the webhook belongs in.
    fn webhook_update(&self, _: Context, _: GuildId, _: ChannelId) {}
}
<|endoftext|>


<commit_before>use rusqlite::{SqliteConnection, SqliteTransaction, SqliteError};
use rusqlite::types::ToSql;
use rustc_serialize::json::{Json, ParserError};
use jmap::util::{FromJson, ParseError};
use jmap::contact::Contact;
use std::error::Error;
use std::convert::From;
use std::fmt;
use self::DbError::*;

use std::path::Path;

const VERSION: u32 = 1;

const CREATE_SQL: [&'static str; 6] = [
r###"
CREATE TABLE records (
    rowid       INTEGER PRIMARY KEY,
    id          TEXT NOT NULL,
    userid      INTEGER NOT NULL,
    type        INTEGER NOT NULL,
    modseq      INTEGER NOT NULL,
    json        TEXT NOT NULL,
    UNIQUE( id, userid )
);
"###,
r###"
CREATE INDEX idx_record_id_userid          ON records ( id, userid );
"###,
r###"
CREATE INDEX idx_record_userid_type        ON records ( userid, type );
"###,
r###"
CREATE INDEX idx_record_userid_type_modseq ON records ( userid, type, modseq );
"###,
r###"
CREATE TABLE modseq (
    userid      INTEGER NOT NULL,
    type        INTEGER NOT NULL,
    modseq      INTEGER NOT NULL,
    UNIQUE( userid, type )
);
"###,
r###"
CREATE INDEX idx_userid_type ON modseq ( userid, type );
"###,
];

/*
const UPGRADE_SQL: [&'static str; 1] = [
    // v1
    ""
];
*/

#[derive(Clone, PartialEq, Debug)]
pub enum DbError {
    InternalError(String),
}

impl Error for DbError {
    fn description(&self) -> &str {
        match *self {
            InternalError(_) => "internal database error",
        }
    }
}

impl fmt::Display for DbError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            InternalError(ref e) => format!("internal database error: {}", e),
        }.to_string())
    }
}

impl From<SqliteError> for DbError {
    fn from(e: SqliteError) -> DbError {
        InternalError(format!("sqlite: {}", e))
    }
}

impl From<ParserError> for DbError {
    fn from(e: ParserError) -> DbError {
        InternalError(format!("json: {}", e))
    }
}

impl From<ParseError> for DbError {
    fn from(e: ParseError) -> DbError {
        InternalError(format!("jmap: {}", e))
    }
}

pub type Transaction<'a> = SqliteTransaction<'a>;

#[derive(Debug)]
pub struct Db {
    conn: SqliteConnection,
}

impl Db {
    pub fn open() -> Result<Db,DbError> {
        //let conn = try!(SqliteConnection::open_in_memory());
        let conn = try!(SqliteConnection::open(&Path::new("db.sqlite")));
        let db = Db {
            conn: conn,
        };

        try!(db.upgrade());

        Ok(db)
    }

    fn transaction(&self) -> Result<Transaction,DbError> {
        match self.conn.transaction() {
            Ok(t) => Ok(t),
            Err(e) => Err(DbError::from(e)),
        }
    }

    fn exec(&self, sql: &str) -> Result<bool,DbError> {
        let mut stmt = try!(self.conn.prepare(sql));
        try!(stmt.execute(&[]));
        Ok(true)
    }

    fn version(&self) -> Result<u32,DbError> {
        let mut stmt = try!(self.conn.prepare("PRAGMA user_version"));
        let mut res = try!(stmt.query(&[]));
        let next = try!(res.next().unwrap());
        let v: i32 = next.get(0);
        Ok(v as u32)
    }

    fn set_version(&self, v: u32) -> Result<bool,DbError> {
        self.exec(format!("PRAGMA user_version = {}", v as i32).as_ref())
    }

    fn upgrade(&self) -> Result<bool,DbError> {
        let txn = try!(self.transaction());

        let ver = try!(self.version());
        if ver == VERSION { return Ok(true) }

        // new database
        if ver == 0 {
            for sql in CREATE_SQL.iter() {
                try!(self.exec(sql));
            }
        }

        /*
        // existing database, upgrade required
        else {
            // XXX
        }
        */

        try!(self.set_version(VERSION));

        try!(txn.commit());

        println!("upgraded db to version {}", VERSION);

        Ok(true)
    }

    pub fn get_records(&self, userid: i64, ids: Option<&Vec<String>>) -> Result<Vec<Contact>,DbError> {
        let objtype = 1; // XXX contacts are type 1 for now

        let mut sql = "SELECT json FROM records WHERE userid = ? AND type = ?".to_string();
        let mut params: Vec<&ToSql> = vec!(&userid, &objtype);

        if let Some(ref ids) = ids {
            sql.push_str(" AND id IN ( ");

            let mut i = ids.iter();
            if let Some(id) = i.next() {
                sql.push_str("?");
                params.push(id);
            }
            for id in i {
                sql.push_str(",?");
                params.push(id);
            }

            sql.push_str(" )");
        }

        let mut stmt = try!(self.conn.prepare(sql.as_ref()));
        let res = try!(stmt.query(params.as_ref()));

        let mut records: Vec<Contact> = Vec::new();
        for row in res {
            if let Ok(ref r) = row {
                let json = try!(Json::from_str((r.get::<String>(0)).as_ref()));
                records.push(try!(Contact::from_json(&json)));
            }
        }

        Ok(records)
    }
}
<commit_msg>Generic func for fetch-single-value DB exec<commit_after>use rusqlite::{SqliteConnection, SqliteTransaction, SqliteError};
use rusqlite::types::{ToSql, FromSql};
use rustc_serialize::json::{Json, ParserError};
use jmap::util::{FromJson, ParseError};
use jmap::contact::Contact;
use std::error::Error;
use std::convert::From;
use std::fmt;
use self::DbError::*;

use std::path::Path;

const VERSION: u32 = 1;

const CREATE_SQL: [&'static str; 6] = [
r###"
CREATE TABLE records (
    rowid       INTEGER PRIMARY KEY,
    id          TEXT NOT NULL,
    userid      INTEGER NOT NULL,
    type        INTEGER NOT NULL,
    modseq      INTEGER NOT NULL,
    json        TEXT NOT NULL,
    UNIQUE( id, userid )
);
"###,
r###"
CREATE INDEX idx_record_id_userid          ON records ( id, userid );
"###,
r###"
CREATE INDEX idx_record_userid_type        ON records ( userid, type );
"###,
r###"
CREATE INDEX idx_record_userid_type_modseq ON records ( userid, type, modseq );
"###,
r###"
CREATE TABLE modseq (
    userid      INTEGER NOT NULL,
    type        INTEGER NOT NULL,
    modseq      INTEGER NOT NULL,
    UNIQUE( userid, type )
);
"###,
r###"
CREATE INDEX idx_userid_type ON modseq ( userid, type );
"###,
];

/*
const UPGRADE_SQL: [&'static str; 1] = [
    // v1
    ""
];
*/

#[derive(Clone, PartialEq, Debug)]
pub enum DbError {
    InternalError(String),
}

impl Error for DbError {
    fn description(&self) -> &str {
        match *self {
            InternalError(_) => "internal database error",
        }
    }
}

impl fmt::Display for DbError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", match *self {
            InternalError(ref e) => format!("internal database error: {}", e),
        }.to_string())
    }
}

impl From<SqliteError> for DbError {
    fn from(e: SqliteError) -> DbError {
        InternalError(format!("sqlite: {}", e))
    }
}

impl From<ParserError> for DbError {
    fn from(e: ParserError) -> DbError {
        InternalError(format!("json: {}", e))
    }
}

impl From<ParseError> for DbError {
    fn from(e: ParseError) -> DbError {
        InternalError(format!("jmap: {}", e))
    }
}

pub type Transaction<'a> = SqliteTransaction<'a>;

#[derive(Debug)]
pub struct Db {
    conn: SqliteConnection,
}

impl Db {
    pub fn open() -> Result<Db,DbError> {
        //let conn = try!(SqliteConnection::open_in_memory());
        let conn = try!(SqliteConnection::open(&Path::new("db.sqlite")));
        let db = Db {
            conn: conn,
        };

        try!(db.upgrade());

        Ok(db)
    }

    fn transaction(&self) -> Result<Transaction,DbError> {
        match self.conn.transaction() {
            Ok(t) => Ok(t),
            Err(e) => Err(DbError::from(e)),
        }
    }

    fn exec(&self, sql: &str) -> Result<bool,DbError> {
        let mut stmt = try!(self.conn.prepare(sql));
        try!(stmt.execute(&[]));
        Ok(true)
    }

    fn exec_value<T>(&self, sql: &str, params: &[&ToSql]) -> Result<Option<T>,DbError> where T: FromSql {
        let mut stmt = try!(self.conn.prepare(sql));
        let mut res = try!(stmt.query(params));

        match res.next() {
            None       => Ok(None),
            Some(next) =>
                match next {
                    Err(e)   => Err(InternalError(format!("sqlite: {}", e))),
                    Ok(next) => {
                        let v: T = next.get(0);
                        Ok(Some(v))
                    },
            }
        }
    }

    fn version(&self) -> Result<u32,DbError> {
        let v = try!(self.exec_value::<i32>("PRAGMA user_version", &[])).unwrap();
        Ok(v as u32)
    }

    fn set_version(&self, v: u32) -> Result<bool,DbError> {
        try!(self.exec(format!("PRAGMA user_version = {}", v as i32).as_ref()));
        Ok(true)
    }

    fn upgrade(&self) -> Result<bool,DbError> {
        let txn = try!(self.transaction());

        let ver = try!(self.version());
        if ver == VERSION { return Ok(true) }

        // new database
        if ver == 0 {
            for sql in CREATE_SQL.iter() {
                try!(self.exec(sql));
            }
        }

        /*
        // existing database, upgrade required
        else {
            // XXX
        }
        */

        try!(self.set_version(VERSION));

        try!(txn.commit());

        println!("upgraded db to version {}", VERSION);

        Ok(true)
    }

    pub fn get_records(&self, userid: i64, ids: Option<&Vec<String>>) -> Result<Vec<Contact>,DbError> {
        let objtype = 1; // XXX contacts are type 1 for now

        let mut sql = "SELECT json FROM records WHERE userid = ? AND type = ?".to_string();
        let mut params: Vec<&ToSql> = vec!(&userid, &objtype);

        if let Some(ref ids) = ids {
            sql.push_str(" AND id IN ( ");

            let mut i = ids.iter();
            if let Some(id) = i.next() {
                sql.push_str("?");
                params.push(id);
            }
            for id in i {
                sql.push_str(",?");
                params.push(id);
            }

            sql.push_str(" )");
        }

        let mut stmt = try!(self.conn.prepare(sql.as_ref()));
        let res = try!(stmt.query(params.as_ref()));

        let mut records: Vec<Contact> = Vec::new();
        for row in res {
            if let Ok(ref r) = row {
                let json = try!(Json::from_str((r.get::<String>(0)).as_ref()));
                records.push(try!(Contact::from_json(&json)));
            }
        }

        Ok(records)
    }
}
<|endoftext|>