<commit_before>use std::fmt::Debug;
use std::hash::{hash,Hash,Hasher,SipHasher};
use std::collections::HashMap;
//use std::thunk::Invoke;
use std::mem::replace;
use std::mem::transmute;
//use std::sync::Arc;
use std::rc::Rc;
use std::fmt;

// "One big memo table" design

pub trait Adapton {
    fn new () -> Self ;

    // Names
    fn name_of_string (self:&mut Self, String) -> Name ;
    fn name_of_u64 (self:&mut Self, u64) -> Name ;
    fn name_pair (self: &Self, Name, Name) -> Name ;
    fn name_fork (self:&mut Self, Name) -> (Name, Name) ;

    // Namespaces
    fn ns<T,F> (self: &mut Self, Name, body:F) -> T
        where F:FnOnce(&mut Self) -> T ;

    // Create immutable, eager arts: put
    fn put<T:Eq+Debug> (self:&mut Self, T) -> Art<T> ;

    // Mutable arts: cell and set
    fn cell<T:Eq+Debug> (self:&mut Self, ArtId, T) -> MutArt<T> ;
    fn set<T:Eq+Debug> (self:&mut Self, MutArt<T>, T) ;

    // Computation arts: thunk
    fn thunk<Arg:Eq+Hash+Debug,T:Eq+Debug>
        (self:&mut Self, id:ArtId,
         fn_body:Box<Fn(Arg) -> T>, arg:Arg) -> Art<T> ;

    // Demand & observe arts: force
    fn force<T:Eq+Debug> (self:&mut Self, Art<T>) -> & T ;
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum Lineage {
    Unknown, Structural,
    Root,
    Pair(Rc<Lineage>,Rc<Lineage>),
    ForkL(Rc<Lineage>),
    ForkR(Rc<Lineage>),
    String(String),
    U64(u64),
    Rc(Rc<Lineage>),
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum Path {
    Empty,
    Child(Rc<Path>,Name),
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub struct Name {
    hash : u64,
    lineage : Rc<Lineage>,
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub struct Loc {
    hash : u64,
    name : Rc<Name>,
    path : Rc<Path>
}

pub enum Node<Res> {
    Pure(PureNode<Res>),
    Mut(MutNode<Res>),
    Compute(ComputeNode<Res>),
}

pub trait OpaqueNode {
    fn loc (self:&Self) -> Rc<Loc> ;
    fn creators (self:&Self) -> Vec<DemPrec> ;
    fn dem_precs (self:&Self) -> Vec<DemPrec> ;
    fn dem_succs (self:&Self) -> Vec<DemSucc> ;
}

impl <Res> OpaqueNode for Node<Res> {
    fn loc (self:&Self) -> Rc<Loc> { panic!("") }
    fn creators (self:&Self) -> Vec<DemPrec> { panic!("") }
    fn dem_precs (self:&Self) -> Vec<DemPrec> { panic!("") }
    fn dem_succs (self:&Self) -> Vec<DemSucc> { panic!("") }
}

pub fn node_of_opaque<'x,Res> (art:Art<Res>, opaque:&'x mut Box<OpaqueNode>) -> &'x mut Box<Node<Res>> {
    panic!("")
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum ArtId {
    None,            // Identifies an Art::Box. No dependency tracking.
    Structural(u64), // Identifies an Art::Loc.
    Nominal(Name),   // Identifies an Art::Loc.
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum Art<T> {
    Box(Box<T>),  // No entry in table. No dependency tracking.
    Loc(Rc<Loc>), // Location in table.
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum MutArt<T> {
    MutArt(Art<T>)
}

#[derive(Debug)]
pub struct PureNode<T> {
    loc : Rc<Loc>,
    val : T,
}

#[derive(Debug)]
pub struct MutNode<T> {
    loc : Rc<Loc>,
    creators  : Vec<DemPrec>,
    dem_precs : Vec<DemPrec>,
    val : T,
}

pub struct ComputeNode<Res> {
    loc : Rc<Loc>,
    creators : Vec<DemPrec>,
    dem_precs : Vec<DemPrec>,
    dem_succs : Vec<DemSucc>,
    res : Option<Res>,
    computer : Box<Computer<Res>>
}

pub trait Computer<Res> {
    fn compute(self:&Self, st:&mut AdaptonState) -> Res;
}

pub trait ComputerArg<Res> {
    type Arg;
    fn get_arg(self:&Self) -> Self::Arg;
    fn compute(self:&Self, st:&mut AdaptonState, arg:Self::Arg) -> Res;
}

impl<Arg,Res> Computer<Res> for ComputerArg<Res,Arg=Arg> {
    fn compute(self:&Self, st:&mut AdaptonState) -> Res {
        self.compute(st, self.get_arg())
    }
}

impl<Res> fmt::Debug for ComputeNode<Res> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(ComputeNode)")
    }
}

#[derive(Debug)]
pub struct DemPrec {
    loc : Rc<Loc>,
}

#[derive(Debug)]
pub struct DemSucc {
    loc : Rc<Loc>,
    dirty : bool
}

pub struct Frame {
    path : Rc<Path>,
    name : Rc<Name>,
    succs : Vec<DemSucc>,
}

pub struct AdaptonState {
    table : HashMap<Rc<Loc>, Box<OpaqueNode>>,
    stack : Vec<Frame>
}

// fn dem_precs<'x,T:'x> (st: &'x mut AdaptonState, loc: &Rc<Loc>) -> &'x mut Vec<DemPrec> {
//     let node = st.table.get(loc) ;
//     match node {
//         None => panic!("dangling pointer"),
//         Some(ptr) => {
//             match *(ptr.get_node()) {
//                 Node::Pure(_) => {
//                     panic!("Node::Pure: no precs")
//                 },
//                 Node::Compute(ref mut nd) => {
//                     &mut nd.dem_precs
//                 },
//                 Node::Mut(ref mut nd) => {
//                     &mut nd.dem_precs
//                 }
//             }
//         }
//     }
// }

pub fn revoke_demand<'x> (st:&mut AdaptonState, src:&Rc<Loc>, succs:&Vec<DemSucc>) {
    // for succ in succs.iter() {
    //     let precs = dem_precs(st, &succ.loc);
    //     precs.retain(|ref prec| &prec.loc != src);
    // }
    panic!("")
}

pub fn invoke_demand<'x> (st:&mut AdaptonState, src:Rc<Loc>, succs:& Vec<DemSucc>) {
    // for succ in succs.iter() {
    //     let precs = dem_precs(st, &succ.loc);
    //     precs.push(DemPrec{loc:src.clone()})
    // }
    panic!("")
}

fn my_hash<T>(obj: T) -> u64
    where T: Hash
{
    let mut hasher = SipHasher::new();
    obj.hash(&mut hasher);
    hasher.finish()
}

impl Adapton for AdaptonState {
    fn new () -> AdaptonState {
        let empty = Rc::new(Path::Empty);
        let root = Rc::new(Name{hash:0, lineage:Rc::new(Lineage::Root) });
        let mut stack = Vec::new();
        stack.push( Frame{path:empty, name:root, succs:Vec::new()} ) ;
        AdaptonState {
            table : HashMap::new (),
            stack : stack,
        }
    }

    fn name_of_string (self:&mut AdaptonState, sym:String) -> Name {
        let h = my_hash(&sym);
        let s = Lineage::String(sym) ;
        Name{ hash:h, lineage:Rc::new(s) }
    }

    fn name_of_u64 (self:&mut AdaptonState, sym:u64) -> Name {
        let h = my_hash(&sym) ;
        let s = Lineage::U64(sym) ;
        Name{ hash:h, lineage:Rc::new(s) }
    }

    fn name_pair (self: &AdaptonState, fst: Name, snd: Name) -> Name {
        let h = my_hash( &(fst.hash,snd.hash) ) ;
        let p = Lineage::Pair(fst.lineage, snd.lineage) ;
        Name{ hash:h, lineage:Rc::new(p) }
    }

    fn name_fork (self:&mut AdaptonState, nm:Name) -> (Name, Name) {
        let h1 = my_hash( &(&nm, 11111111) ) ; // TODO: make this hashing better.
        let h2 = my_hash( &(&nm, 22222222) ) ;
        ( Name{ hash:h1,
                lineage:Rc::new(Lineage::ForkL(nm.lineage.clone())) } ,
          Name{ hash:h2,
                lineage:Rc::new(Lineage::ForkR(nm.lineage)) } )
    }

    fn ns<T,F> (self: &mut Self, nm:Name, body:F) -> T where F:FnOnce(&mut Self) -> T {
        let path_body = Rc::new(Path::Child(self.stack[0].path.clone(), nm)) ;
        let path_pre = replace(&mut self.stack[0].path, path_body ) ;
        let x = body(self) ;
        let path_body = replace(&mut self.stack[0].path, path_pre) ;
        drop(path_body);
        x
    }

    fn put<T:Eq> (self:&mut AdaptonState, x:T) -> Art<T> {
        Art::Box(Box::new(x))
    }

    fn cell<T:Eq+Debug> (self:&mut AdaptonState, id:ArtId, val:T) -> MutArt<T> {
        let loc = match id {
            ArtId::None => panic!("a cell requires a unique identity"),
            ArtId::Structural(hash) => {
                Rc::new(Loc{
                    hash:hash,
                    name:Rc::new(Name{hash:hash,
                                      lineage:Rc::new(Lineage::Structural)}),
                    path:self.stack[0].path.clone()
                })
            }
            ArtId::Nominal(nm) => {
                Rc::new(Loc{
                    hash:nm.hash,
                    name:Rc::new(nm),
                    path:self.stack[0].path.clone() })
            }} ;
        let mut node = Node::Mut(MutNode{
            loc:loc.clone(),
            dem_precs:Vec::new(),
            creators:Vec::new(),
            val:val
        }) ;
        // TODO: Check to see if the cell exists;
        // check if its content has changed.
        // dirty its precs if so.

        // self.table.insert(loc.clone(), Box::new(node)) ;
        self.table.insert(loc.clone(), panic!("Box::new(node)")) ;

        MutArt::MutArt(Art::Loc(loc))
    }

    fn set<T:Eq+Debug> (self:&mut Self, cell:MutArt<T>, val:T) {
    }

    fn thunk<Arg:Eq+Hash+Debug,T:Eq+Debug>
        (self:&mut AdaptonState,
         id:ArtId, fn_body:Box<Fn(Arg)->T>, arg:Arg) -> Art<T>
    {
        match id {
            ArtId::None => {
                Art::Box(Box::new(fn_body((arg))))
            },
            ArtId::Structural(hash) => {
                panic!("")
            },
            ArtId::Nominal(nm) => {
                panic!("")
            }
        }
    }

    fn force<T:Eq+Debug> (self:&mut AdaptonState, art:Art<T>) -> & T {
        panic!("")
        // match art {
        //     Art::Box(b) => & b,
        //     Art::Loc(loc) => {
        //         let node = self.table.get_mut(&loc) ;
        //         match node {
        //             None => panic!("dangling pointer"),
        //             Some(ref ptr) => {
        //                 let node : &mut Node<T> = unsafe {
        //                     let node : *mut Node<T> = panic!("transmute::<*mut (), *mut Node<T>>(*ptr)") ;
        //                     &mut ( *node ) } ;
        //                 match *node {
        //                     Node::Pure(ref mut nd) => {
        //                         & nd.val
        //                     },
        //                     Node::Mut(ref mut nd) => {
        //                         if self.stack.is_empty() { } else {
        //                             self.stack[0].succs.push(DemSucc{loc:loc.clone(),dirty:false});
        //                         } ;
        //                         & nd.val
        //                     },
        //                     Node::Compute(ref mut nd) => {
        //                         self.stack.push ( Frame{name:loc.name.clone(),
        //                                                 path:loc.path.clone(),
        //                                                 succs:Vec::new(), } );
        //                         let val = panic!("TODO: run compute node body") ;
        //                         let mut frame = match
        //                             self.stack.pop() { None => panic!(""), Some(frame) => frame } ;
        //                         revoke_demand( self, &nd.loc, &nd.dem_succs );
        //                         invoke_demand( self, nd.loc.clone(), &frame.succs );
        //                         nd.dem_succs = frame.succs;
        //                         if self.stack.is_empty() { } else {
        //                             self.stack[0].succs.push(DemSucc{loc:loc.clone(),dirty:false});
        //                         };
        //                         & val
        //                     }
        //                 }
        //             }
        //         }
        //     }
        // }
    }
}

pub fn main () {

}
<commit_msg>Link to RWH's PL foundations book.<commit_after>use std::fmt::Debug;
use std::hash::{hash,Hash,Hasher,SipHasher};
use std::collections::HashMap;
//use std::thunk::Invoke;
use std::mem::replace;
use std::mem::transmute;
//use std::sync::Arc;
use std::rc::Rc;
use std::fmt;

// "One big memo table" design

pub trait Adapton {
    fn new () -> Self ;

    // Names
    fn name_of_string (self:&mut Self, String) -> Name ;
    fn name_of_u64 (self:&mut Self, u64) -> Name ;
    fn name_pair (self: &Self, Name, Name) -> Name ;
    fn name_fork (self:&mut Self, Name) -> (Name, Name) ;

    // Namespaces
    fn ns<T,F> (self: &mut Self, Name, body:F) -> T
        where F:FnOnce(&mut Self) -> T ;

    // Create immutable, eager arts: put
    fn put<T:Eq+Debug> (self:&mut Self, T) -> Art<T> ;

    // Mutable arts: cell and set
    fn cell<T:Eq+Debug> (self:&mut Self, ArtId, T) -> MutArt<T> ;
    fn set<T:Eq+Debug> (self:&mut Self, MutArt<T>, T) ;

    // Computation arts: thunk
    fn thunk<Arg:Eq+Hash+Debug,T:Eq+Debug>
        (self:&mut Self, id:ArtId,
         fn_body:Box<Fn(Arg) -> T>, arg:Arg) -> Art<T> ;

    // Demand & observe arts (all kinds): force
    fn force<T:Eq+Debug> (self:&mut Self, Art<T>) -> & T ;
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum Lineage {
    // Roots: Unique Symbols (String, U64)
    // (See Chapter 31 of PFPL 2nd Edition. Harper 2015.
        http://www.cs.cmu.edu/~rwh/plbook/2nded.pdf)
    String(String),
    U64(u64),
    // Non-Roots: Pair, ForkL, ForkR, Rc:
    Pair(Rc<Lineage>,Rc<Lineage>),
    ForkL(Rc<Lineage>),
    ForkR(Rc<Lineage>),
    Rc(Rc<Lineage>),
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum Path {
    Empty,
    Child(Rc<Path>,Name),
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub struct Name {
    hash : u64,
    lineage : Rc<Lineage>,
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub struct Loc {
    hash : u64,
    name : Rc<Name>,
    path : Rc<Path>
}

pub enum Node<Res> {
    Pure(PureNode<Res>),
    Mut(MutNode<Res>),
    Compute(ComputeNode<Res>),
}

pub trait OpaqueNode {
    fn loc (self:&Self) -> Rc<Loc> ;
    fn creators (self:&Self) -> Vec<DemPrec> ;
    fn dem_precs (self:&Self) -> Vec<DemPrec> ;
    fn dem_succs (self:&Self) -> Vec<DemSucc> ;
}

impl <Res> OpaqueNode for Node<Res> {
    fn loc (self:&Self) -> Rc<Loc> { panic!("") }
    fn creators (self:&Self) -> Vec<DemPrec> { panic!("") }
    fn dem_precs (self:&Self) -> Vec<DemPrec> { panic!("") }
    fn dem_succs (self:&Self) -> Vec<DemSucc> { panic!("") }
}

pub fn node_of_opaque<'x,Res> (art:Art<Res>, opaque:&'x mut Box<OpaqueNode>) -> &'x mut Box<Node<Res>> {
    panic!("")
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum ArtId {
    None,            // Identifies an Art::Box. No dependency tracking.
    Structural(u64), // Identifies an Art::Loc.
    Nominal(Name),   // Identifies an Art::Loc.
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum Art<T> {
    Box(Box<T>),  // No entry in table. No dependency tracking.
    Loc(Rc<Loc>), // Location in table.
}

#[derive(Hash,Debug,PartialEq,Eq)]
pub enum MutArt<T> {
    MutArt(Art<T>)
}

#[derive(Debug)]
pub struct PureNode<T> {
    loc : Rc<Loc>,
    val : T,
}

#[derive(Debug)]
pub struct MutNode<T> {
    loc : Rc<Loc>,
    creators  : Vec<DemPrec>,
    dem_precs : Vec<DemPrec>,
    val : T,
}

pub struct ComputeNode<Res> {
    loc : Rc<Loc>,
    creators : Vec<DemPrec>,
    dem_precs : Vec<DemPrec>,
    dem_succs : Vec<DemSucc>,
    res : Option<Res>,
    computer : Box<Computer<Res>>
}

pub trait Computer<Res> {
    fn compute(self:&Self, st:&mut AdaptonState) -> Res;
}

pub trait ComputerArg<Res> {
    type Arg;
    fn get_arg(self:&Self) -> Self::Arg;
    fn compute(self:&Self, st:&mut AdaptonState, arg:Self::Arg) -> Res;
}

impl<Arg,Res> Computer<Res> for ComputerArg<Res,Arg=Arg> {
    fn compute(self:&Self, st:&mut AdaptonState) -> Res {
        self.compute(st, self.get_arg())
    }
}

impl<Res> fmt::Debug for ComputeNode<Res> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "(ComputeNode)")
    }
}

#[derive(Debug)]
pub struct DemPrec {
    loc : Rc<Loc>,
}

#[derive(Debug)]
pub struct DemSucc {
    loc : Rc<Loc>,
    dirty : bool
}

pub struct Frame {
    path : Rc<Path>,
    name : Rc<Name>,
    succs : Vec<DemSucc>,
}

pub struct AdaptonState {
    table : HashMap<Rc<Loc>, Box<OpaqueNode>>,
    stack : Vec<Frame>
}

// fn dem_precs<'x,T:'x> (st: &'x mut AdaptonState, loc: &Rc<Loc>) -> &'x mut Vec<DemPrec> {
//     let node = st.table.get(loc) ;
//     match node {
//         None => panic!("dangling pointer"),
//         Some(ptr) => {
//             match *(ptr.get_node()) {
//                 Node::Pure(_) => {
//                     panic!("Node::Pure: no precs")
//                 },
//                 Node::Compute(ref mut nd) => {
//                     &mut nd.dem_precs
//                 },
//                 Node::Mut(ref mut nd) => {
//                     &mut nd.dem_precs
//                 }
//             }
//         }
//     }
// }

pub fn revoke_demand<'x> (st:&mut AdaptonState, src:&Rc<Loc>, succs:&Vec<DemSucc>) {
    // for succ in succs.iter() {
    //     let precs = dem_precs(st, &succ.loc);
    //     precs.retain(|ref prec| &prec.loc != src);
    // }
    panic!("")
}

pub fn invoke_demand<'x> (st:&mut AdaptonState, src:Rc<Loc>, succs:& Vec<DemSucc>) {
    // for succ in succs.iter() {
    //     let precs = dem_precs(st, &succ.loc);
    //     precs.push(DemPrec{loc:src.clone()})
    // }
    panic!("")
}

fn my_hash<T>(obj: T) -> u64
    where T: Hash
{
    let mut hasher = SipHasher::new();
    obj.hash(&mut hasher);
    hasher.finish()
}

impl Adapton for AdaptonState {
    fn new () -> AdaptonState {
        let empty = Rc::new(Path::Empty);
        let root = Rc::new(Name{hash:0, lineage:Rc::new(Lineage::Root) });
        let mut stack = Vec::new();
        stack.push( Frame{path:empty, name:root, succs:Vec::new()} ) ;
        AdaptonState {
            table : HashMap::new (),
            stack : stack,
        }
    }

    fn name_of_string (self:&mut AdaptonState, sym:String) -> Name {
        let h = my_hash(&sym);
        let s = Lineage::String(sym) ;
        Name{ hash:h, lineage:Rc::new(s) }
    }

    fn name_of_u64 (self:&mut AdaptonState, sym:u64) -> Name {
        let h = my_hash(&sym) ;
        let s = Lineage::U64(sym) ;
        Name{ hash:h, lineage:Rc::new(s) }
    }

    fn name_pair (self: &AdaptonState, fst: Name, snd: Name) -> Name {
        let h = my_hash( &(fst.hash,snd.hash) ) ;
        let p = Lineage::Pair(fst.lineage, snd.lineage) ;
        Name{ hash:h, lineage:Rc::new(p) }
    }

    fn name_fork (self:&mut AdaptonState, nm:Name) -> (Name, Name) {
        let h1 = my_hash( &(&nm, 11111111) ) ; // TODO: make this hashing better.
        let h2 = my_hash( &(&nm, 22222222) ) ;
        ( Name{ hash:h1,
                lineage:Rc::new(Lineage::ForkL(nm.lineage.clone())) } ,
          Name{ hash:h2,
                lineage:Rc::new(Lineage::ForkR(nm.lineage)) } )
    }

    fn ns<T,F> (self: &mut Self, nm:Name, body:F) -> T where F:FnOnce(&mut Self) -> T {
        let path_body = Rc::new(Path::Child(self.stack[0].path.clone(), nm)) ;
        let path_pre = replace(&mut self.stack[0].path, path_body ) ;
        let x = body(self) ;
        let path_body = replace(&mut self.stack[0].path, path_pre) ;
        drop(path_body);
        x
    }

    fn put<T:Eq> (self:&mut AdaptonState, x:T) -> Art<T> {
        Art::Box(Box::new(x))
    }

    fn cell<T:Eq+Debug> (self:&mut AdaptonState, id:ArtId, val:T) -> MutArt<T> {
        let loc = match id {
            ArtId::None => panic!("a cell requires a unique identity"),
            ArtId::Structural(hash) => {
                Rc::new(Loc{
                    hash:hash,
                    name:Rc::new(Name{hash:hash,
                                      lineage:Rc::new(Lineage::Structural)}),
                    path:self.stack[0].path.clone()
                })
            }
            ArtId::Nominal(nm) => {
                Rc::new(Loc{
                    hash:nm.hash,
                    name:Rc::new(nm),
                    path:self.stack[0].path.clone() })
            }} ;
        let mut node = Node::Mut(MutNode{
            loc:loc.clone(),
            dem_precs:Vec::new(),
            creators:Vec::new(),
            val:val
        }) ;
        // TODO: Check to see if the cell exists;
        // check if its content has changed.
        // dirty its precs if so.

        // self.table.insert(loc.clone(), Box::new(node)) ;
        self.table.insert(loc.clone(), panic!("Box::new(node)")) ;

        MutArt::MutArt(Art::Loc(loc))
    }

    fn set<T:Eq+Debug> (self:&mut Self, cell:MutArt<T>, val:T) {
    }

    fn thunk<Arg:Eq+Hash+Debug,T:Eq+Debug>
        (self:&mut AdaptonState,
         id:ArtId, fn_body:Box<Fn(Arg)->T>, arg:Arg) -> Art<T>
    {
        match id {
            ArtId::None => {
                Art::Box(Box::new(fn_body((arg))))
            },
            ArtId::Structural(hash) => {
                panic!("")
            },
            ArtId::Nominal(nm) => {
                panic!("")
            }
        }
    }

    fn force<T:Eq+Debug> (self:&mut AdaptonState, art:Art<T>) -> & T {
        panic!("")
        // match art {
        //     Art::Box(b) => & b,
        //     Art::Loc(loc) => {
        //         let node = self.table.get_mut(&loc) ;
        //         match node {
        //             None => panic!("dangling pointer"),
        //             Some(ref ptr) => {
        //                 let node : &mut Node<T> = unsafe {
        //                     let node : *mut Node<T> = panic!("transmute::<*mut (), *mut Node<T>>(*ptr)") ;
        //                     &mut ( *node ) } ;
        //                 match *node {
        //                     Node::Pure(ref mut nd) => {
        //                         & nd.val
        //                     },
        //                     Node::Mut(ref mut nd) => {
        //                         if self.stack.is_empty() { } else {
        //                             self.stack[0].succs.push(DemSucc{loc:loc.clone(),dirty:false});
        //                         } ;
        //                         & nd.val
        //                     },
        //                     Node::Compute(ref mut nd) => {
        //                         self.stack.push ( Frame{name:loc.name.clone(),
        //                                                 path:loc.path.clone(),
        //                                                 succs:Vec::new(), } );
        //                         let val = panic!("TODO: run compute node body") ;
        //                         let mut frame = match
        //                             self.stack.pop() { None => panic!(""), Some(frame) => frame } ;
        //                         revoke_demand( self, &nd.loc, &nd.dem_succs );
        //                         invoke_demand( self, nd.loc.clone(), &frame.succs );
        //                         nd.dem_succs = frame.succs;
        //                         if self.stack.is_empty() { } else {
        //                             self.stack[0].succs.push(DemSucc{loc:loc.clone(),dirty:false});
        //                         };
        //                         & val
        //                     }
        //                 }
        //             }
        //         }
        //     }
        // }
    }
}

pub fn main () {

}
<|endoftext|>


<commit_before>use std::slice::Iter;
use rng;

#[derive(PartialEq, Debug, Copy, Clone)]
enum Tile {
    Unused,
    Floor,
    Corridor,
    Wall,
    ClosedDoor,
    OpenDoor,
    Exit,
    Entrance,
}

#[derive(Debug, PartialEq)]
enum Dir {
    North,
    South,
    East,
    West,
}

impl Dir {
    // iterator over direction types
    pub fn iterator() -> Iter<'static, Dir> {
        static DIR: [Dir;  4] = [Dir::North, Dir::South, Dir::East, Dir::West];
        DIR.into_iter()
    }

    pub fn get_random_dir<'a>()-> &'a Dir {
        Dir::iterator().nth(rng::inclusive_random(0, 3) as usize).unwrap()
    }
}

#[derive(Debug, Copy, Clone)]
struct Rect {
    x: isize,
    y: isize,
    width: isize,
    height: isize,
}

impl Rect {
    fn new(x: isize, y: isize, width: isize, height: isize) -> Rect {
        Rect { x: x, y: y, width: width, height: height }
    }
}

pub struct Dungeon {
    width: isize,
    height: isize,
    tiles: Vec<Tile>,
    rooms: Vec<Rect>,
    exits: Vec<Rect>,
}

impl Dungeon {
    pub fn new(width: isize, height: isize) -> Dungeon {
        let mut tiles = Vec::new();
        for _x in 1..width*height+1 {
            tiles.push(Tile::Unused);
        }

        Dungeon { width: width, height: height, tiles: tiles, rooms: Vec::new(),
                    exits: Vec::new() }
    }

    pub fn generate(&mut self, maxfeatures: isize) {
        let x = self.width;
        let y = self.height;
        if !self.make_room(x / 2, y / 2, Dir::get_random_dir(), true) {
            println!("unable to place first room!");
        }

        for x in 1..maxfeatures {
            if !self.has_exits() {
                println!("unable to place more features, placed {}.", x);
                break;
            }
        }

        if !self.place_object(Tile::Exit) {
            println!("unable to place exit");
        }

        if !self.place_object(Tile::Entrance) {
            println!("unable to place entrance");
        }
    }

    fn get_tile(&self, x: isize, y: isize) -> Tile {
        if (x < 0) || (y < 0) || (x >= self.width as isize) || (y >= self.height as isize) {
            return Tile::Unused
        }

        self.tiles[x as usize + y as usize * self.width as usize]
    }

    fn set_tile(&mut self, x: isize, y: isize, tile: Tile) {
        self.tiles[x as usize + y as usize * self.width as usize] = tile;
    }

    fn has_exits(&mut self) -> bool {
        for _i in 0..1000 {
            if self.exits.is_empty() {
                break;
            }

            // pick a random side of a room/corridor
            let r: isize = rng::exclusive_random(self.exits.len() as isize);
            let x: isize = rng::inclusive_random(self.exits[r as usize].x, self.exits[r as usize].x +
                self.exits[r as usize].width - 1);
            let y: isize = rng::inclusive_random(self.exits[r as usize].y, self.exits[r as usize].y +
                self.exits[r as usize].height - 1);

            for dir in Dir::iterator() {
                if self.create_feature(x, y, dir) {
                    self.exits.remove(r as usize);
                    return true
                }
            }
        }
        false
    }

    fn create_feature(&mut self, x: isize, y: isize, dir: &Dir) -> bool {
        let room_chance: isize = 50;
        let mut dx: isize = 0;
        let mut dy: isize = 0;

        if *dir == Dir::North {
            dy = 1;
        }

        else if *dir == Dir::South {
            dy = -1;
        }

        else if *dir == Dir::East {
            dx = -1;
        }

        else if *dir == Dir::West {
            dx = 1;
        }

        if self.get_tile(x as isize + dx, y as isize + dy) != Tile::Floor && self.get_tile(x as isize + dx, y as isize + dy) != Tile::Corridor {
            return false
        }

        if rng::exclusive_random(100) < room_chance {
            if self.make_room(x, y, &dir, false) {
                self.set_tile(x, y, Tile::ClosedDoor);

                return true
            }
        }

        else {
            if self.make_corridor(x, y, dir) {
                if self.get_tile(x as isize + dx, y as isize + dy) == Tile::Floor {
                    self.set_tile(x, y, Tile::ClosedDoor);
                }

                else {
                    self.set_tile(x, y, Tile::Corridor);

                    return true
                }
            }
        }
        return false
    }

    fn make_room(&mut self, x: isize, y: isize, dir: &Dir, firstroom: bool) -> bool {
        let minsize: isize = 3;
        let maxsize: isize = 6;

        let mut room: Rect = Rect::new(0, 0, rng::inclusive_random(minsize, maxsize), rng::inclusive_random(minsize, maxsize));

        if *dir == Dir::North {
            room.x = x - room.width / 2;
            room.y = y - room.height;
        }

        else if *dir == Dir::South {
            room.x = x - room.width /2;
            room.y = y + 1;
        }

        else if *dir == Dir::East {
            room.x = x + 1;
            room.y = y - room.height / 2;
        }

        else if *dir == Dir::West {
            room.x = x - room.width;
            room.y = y - room.height / 2;
        }

        if self.place_rect(&room, Tile::Floor) {
            self.rooms.push(room);

            if *dir != Dir::South || firstroom {
                self.exits.push(Rect::new(room.x, room.y - 1, room.width, 1));
            }

            if *dir != Dir::North || firstroom {
                self.exits.push(Rect::new(room.x, room.y + room.height, room.width, 1));
            }

            if *dir != Dir::East || firstroom {
                self.exits.push(Rect::new(room.x - 1, room.y, 1, room.height));
            }

            if *dir != Dir::West || firstroom {
                self.exits.push(Rect::new(room.x + room.width, room.y, 1, room.height));
            }

            return true
        }

        return false
    }

    fn make_corridor(&mut self, x: isize, y: isize, dir: &Dir) -> bool {
        use rand::{thread_rng, Rng};
        let mut rng = thread_rng();

        let minlength = 3;
        let maxlength = 6;

        let mut corridor = Rect::new(x, y, 0, 0);

        if rng.gen() { // horizontal
            corridor.width = rng::inclusive_random(minlength, maxlength);
            corridor.height = 1;

            if *dir == Dir::North {
                corridor.y = y - 1;
                if rng.gen() { // west
                    corridor.x = x - corridor.width + 1;
                }
            }

            else if *dir == Dir::South {
                corridor.y = y + 1;

                if rng.gen() { // west
                    corridor.x = x - corridor.width + 1;
                }
            }

            else if *dir == Dir::East {
                corridor.x = x + 1;
            }

            else if *dir == Dir::West {
                corridor.x = x - corridor.width + 1;
            }
        }

        else { // vertical
            corridor.width = 1;
            corridor.height = rng::inclusive_random(minlength, maxlength);

            if *dir == Dir::North {
                corridor.y = y - corridor.height;
            }

            else if *dir == Dir::South {
                corridor.y = y + 1;
            }

            else if *dir == Dir::East {
                corridor.x = x + 1;

                if rng.gen() { // north
                    corridor.y = y - corridor.height + 1;
                }
            }

            else if *dir == Dir::West {
                corridor.x = x - 1;

                if rng.gen() { // north
                    corridor.y = y - corridor.height + 1;
                }
            }
        }

        if self.place_rect(&corridor, Tile::Corridor) {
            if *dir != Dir::South && corridor.width != 1 { // north side
                self.exits.push(Rect::new(corridor.x, corridor.y - 1, corridor.width, 1));
            }

            if *dir != Dir::North && corridor.width != 1 { // south side
                self.exits.push(Rect::new(corridor.x, corridor.y + corridor.height, corridor.width, 1));
            }

            if *dir != Dir::East && corridor.height != 1 { // west side
                self.exits.push(Rect::new(corridor.x - 1, corridor.y, 1, corridor.height));
            }

            if *dir != Dir::West && corridor.height != 1 { // east side
                self.exits.push(Rect::new(corridor.x + corridor.width, corridor.y, 1, corridor.height));
            }

            return true
        }

        return false
    }

    fn place_rect(&mut self, rect: &Rect, tile: Tile) -> bool {
        if (rect.x < 1) || (rect.y < 1) || (rect.x + rect.width > self.width - 1) || (rect.y + rect.height > self.height - 1) {
            return false
        }

        for y in rect.y..rect.y+rect.height {
            for x in rect.x..rect.x + rect.width {
                if self.get_tile(x, y) != Tile::Unused {
                    return false // this area is already in use
                }
            }
        }

        for y in rect.y-1..rect.y+rect.height+1 {
            for x in rect.x-1..rect.x+rect.width+1 {
                if (x == rect.x - 1) || (y == rect.y - 1) || (x == rect.x + rect.width) || (y == rect.y + rect.height) {
                    self.set_tile(x, y, Tile::Wall);
                }

                else {
                    self.set_tile(x, y, tile);
                }
            }
        }

        return true
    }

    fn place_object(&mut self, tile: Tile) -> bool {
        if self.rooms.is_empty() {
            return false
        }

        let r: isize = rng::exclusive_random(self.rooms.len() as isize);
        let x: isize = rng::inclusive_random(self.rooms[r as usize].x + 1, self.rooms[r as usize].x + self.rooms[r as usize].width - 2);
        let y: isize = rng::inclusive_random(self.rooms[r as usize].y + 1, self.rooms[r as usize].y + self.rooms[r as usize].height - 2);

        if self.get_tile(x, y) == Tile::Floor {
            self.set_tile(x, y, tile);

            self.rooms.remove(r as usize);

            return true
        }
        return false;
    }
}

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

    #[test]
    fn test_dungeon() {
        let mut d: Dungeon = Dungeon::new(25, 25);
        let max_features: isize = 10;

        d.generate(max_features);
    }
}<commit_msg>added print function/fixed boundaries<commit_after>use std::slice::Iter;
use rng;

#[derive(PartialEq, Debug, Copy, Clone)]
enum Tile {
    Unused,
    Floor,
    Corridor,
    Wall,
    ClosedDoor,
    OpenDoor,
    Exit,
    Entrance,
}

#[derive(Debug, PartialEq)]
enum Dir {
    North,
    South,
    East,
    West,
}

impl Dir {
    // iterator over direction types
    pub fn iterator() -> Iter<'static, Dir> {
        static DIR: [Dir;  4] = [Dir::North, Dir::South, Dir::East, Dir::West];
        DIR.into_iter()
    }

    pub fn get_random_dir<'a>()-> &'a Dir {
        Dir::iterator().nth(rng::inclusive_random(0, 3) as usize).unwrap()
    }
}

#[derive(Debug, Copy, Clone)]
struct Rect {
    x: isize,
    y: isize,
    width: isize,
    height: isize,
}

impl Rect {
    fn new(x: isize, y: isize, width: isize, height: isize) -> Rect {
        Rect { x: x, y: y, width: width, height: height }
    }
}

pub struct Dungeon {
    width: isize,
    height: isize,
    tiles: Vec<Tile>,
    rooms: Vec<Rect>,
    exits: Vec<Rect>,
}

impl Dungeon {
    pub fn new(width: isize, height: isize) -> Dungeon {
        let mut tiles = Vec::new();
        for _x in 1..width*height+1 {
            tiles.push(Tile::Unused);
        }

        Dungeon { width: width, height: height, tiles: tiles, rooms: Vec::new(),
                    exits: Vec::new() }
    }

    fn _print_dungeon(&self) {
        for y in 1..self.height {
            for x in 1..self.width {
                print!("{}", self._get_tile_icon(self.get_tile(x, y)));
            }
            println!("");
        }
    }

    fn _get_tile_icon(&self, tile: Tile) -> char {
        match tile {
            Tile::Floor =>      '.',
            Tile::Corridor =>   ',',
            Tile::Wall =>       '#',
            Tile::ClosedDoor => '+',
            Tile::OpenDoor =>   '-',
            Tile::Exit =>       '>',
            Tile::Entrance =>   '<',
            _ =>                ' ',
        }
    }

    pub fn generate(&mut self, maxfeatures: isize) {
        let x = self.width;
        let y = self.height;
        if !self.make_room(x / 2, y / 2, Dir::get_random_dir(), true) {
            println!("unable to place first room!");
        }

        for x in 1..maxfeatures {
            if !self.has_exits() {
                println!("unable to place more features, placed {}.", x);
                break;
            }
        }

        if !self.place_object(Tile::Exit) {
            println!("unable to place exit");
        }

        if !self.place_object(Tile::Entrance) {
            println!("unable to place entrance");
        }
    }

    fn get_tile(&self, x: isize, y: isize) -> Tile {
        if (x < 0) || (y < 0) || (x >= self.width as isize) || (y >= self.height as isize) {
            return Tile::Unused
        }

        self.tiles[x as usize + y as usize * self.width as usize]
    }

    fn set_tile(&mut self, x: isize, y: isize, tile: Tile) {
        self.tiles[x as usize + y as usize * self.width as usize] = tile;
    }

    fn has_exits(&mut self) -> bool {
        for _i in 0..1000 {
            if self.exits.is_empty() {
                break;
            }

            // pick a random side of a room/corridor
            let r: isize = rng::exclusive_random(self.exits.len() as isize);
            let x: isize = rng::inclusive_random(self.exits[r as usize].x, self.exits[r as usize].x +
                self.exits[r as usize].width - 1);
            let y: isize = rng::inclusive_random(self.exits[r as usize].y, self.exits[r as usize].y +
                self.exits[r as usize].height - 1);

            for dir in Dir::iterator() {
                if self.create_feature(x, y, dir) {
                    self.exits.remove(r as usize);
                    return true
                }
            }
        }
        false
    }

    fn create_feature(&mut self, x: isize, y: isize, dir: &Dir) -> bool {
        let room_chance: isize = 50;
        let mut dx: isize = 0;
        let mut dy: isize = 0;

        if *dir == Dir::North {
            dy = 1;
        }

        else if *dir == Dir::South {
            dy = -1;
        }

        else if *dir == Dir::East {
            dx = -1;
        }

        else if *dir == Dir::West {
            dx = 1;
        }

        if self.get_tile(x as isize + dx, y as isize + dy) != Tile::Floor && self.get_tile(x as isize + dx, y as isize + dy) != Tile::Corridor {
            return false
        }

        if rng::exclusive_random(100) < room_chance {
            if self.make_room(x, y, &dir, false) {
                self.set_tile(x, y, Tile::ClosedDoor);

                return true
            }
        }

        else {
            if self.make_corridor(x, y, dir) {
                if self.get_tile(x as isize + dx, y as isize + dy) == Tile::Floor {
                    self.set_tile(x, y, Tile::ClosedDoor);
                }

                else {
                    self.set_tile(x, y, Tile::Corridor);

                    return true
                }
            }
        }
        return false
    }

    fn make_room(&mut self, x: isize, y: isize, dir: &Dir, firstroom: bool) -> bool {
        let minsize: isize = 3;
        let maxsize: isize = 16;

        let mut room: Rect = Rect::new(0, 0, rng::inclusive_random(minsize, maxsize), rng::inclusive_random(minsize, maxsize));

        if *dir == Dir::North {
            room.x = x - room.width / 2;
            room.y = y - room.height;
        }

        else if *dir == Dir::South {
            room.x = x - room.width /2;
            room.y = y + 1;
        }

        else if *dir == Dir::East {
            room.x = x + 1;
            room.y = y - room.height / 2;
        }

        else if *dir == Dir::West {
            room.x = x - room.width;
            room.y = y - room.height / 2;
        }

        if self.place_rect(&room, Tile::Floor) {
            self.rooms.push(room);

            if *dir != Dir::South || firstroom {
                self.exits.push(Rect::new(room.x, room.y - 1, room.width, 1));
            }

            if *dir != Dir::North || firstroom {
                self.exits.push(Rect::new(room.x, room.y + room.height, room.width, 1));
            }

            if *dir != Dir::East || firstroom {
                self.exits.push(Rect::new(room.x - 1, room.y, 1, room.height));
            }

            if *dir != Dir::West || firstroom {
                self.exits.push(Rect::new(room.x + room.width, room.y, 1, room.height));
            }

            return true
        }

        return false
    }

    fn make_corridor(&mut self, x: isize, y: isize, dir: &Dir) -> bool {
        use rand::{thread_rng, Rng};
        let mut rng = thread_rng();

        let minlength = 3;
        let maxlength = 10;

        let mut corridor = Rect::new(x, y, 0, 0);

        if rng.gen() { // horizontal
            corridor.width = rng::inclusive_random(minlength, maxlength);
            corridor.height = 1;

            if *dir == Dir::North {
                corridor.y = y - 1;
                if rng.gen() { // west
                    corridor.x = x - corridor.width + 1;
                }
            }

            else if *dir == Dir::South {
                corridor.y = y + 1;

                if rng.gen() { // west
                    corridor.x = x - corridor.width + 1;
                }
            }

            else if *dir == Dir::East {
                corridor.x = x + 1;
            }

            else if *dir == Dir::West {
                corridor.x = x - corridor.width + 1;
            }
        }

        else { // vertical
            corridor.width = 1;
            corridor.height = rng::inclusive_random(minlength, maxlength);

            if *dir == Dir::North {
                corridor.y = y - corridor.height;
            }

            else if *dir == Dir::South {
                corridor.y = y + 1;
            }

            else if *dir == Dir::East {
                corridor.x = x + 1;

                if rng.gen() { // north
                    corridor.y = y - corridor.height + 1;
                }
            }

            else if *dir == Dir::West {
                corridor.x = x - 1;

                if rng.gen() { // north
                    corridor.y = y - corridor.height + 1;
                }
            }
        }

        if self.place_rect(&corridor, Tile::Corridor) {
            if *dir != Dir::South && corridor.width != 1 { // north side
                self.exits.push(Rect::new(corridor.x, corridor.y - 1, corridor.width, 1));
            }

            if *dir != Dir::North && corridor.width != 1 { // south side
                self.exits.push(Rect::new(corridor.x, corridor.y + corridor.height, corridor.width, 1));
            }

            if *dir != Dir::East && corridor.height != 1 { // west side
                self.exits.push(Rect::new(corridor.x - 1, corridor.y, 1, corridor.height));
            }

            if *dir != Dir::West && corridor.height != 1 { // east side
                self.exits.push(Rect::new(corridor.x + corridor.width, corridor.y, 1, corridor.height));
            }

            return true
        }

        return false
    }

    fn place_rect(&mut self, rect: &Rect, tile: Tile) -> bool {
        // ensure rect is placed within the boundaries of the dungeon
        if (rect.x <= 1) || (rect.y <= 1) || (rect.x + rect.width > self.width - 1) || (rect.y + rect.height > self.height - 1) {
            return false
        }

        for y in rect.y..rect.y+rect.height {
            for x in rect.x..rect.x + rect.width {
                if self.get_tile(x, y) != Tile::Unused {
                    return false // this area is already in use
                }
            }
        }

        // checks have passed, we can place a rect here
        for y in rect.y-1..rect.y+rect.height+1 {
            for x in rect.x-1..rect.x+rect.width+1 {
                // fill boundaries of rect with walls
                if (x == rect.x - 1) || (y == rect.y - 1) || (x == rect.x + rect.width) || (y == rect.y + rect.height) {
                    self.set_tile(x, y, Tile::Wall);
                }

                // fill rect with appropriate tiles
                else {
                    self.set_tile(x, y, tile);
                }
            }
        }

        return true
    }

    fn place_object(&mut self, tile: Tile) -> bool {
        if self.rooms.is_empty() {
            return false
        }

        let r: isize = rng::exclusive_random(self.rooms.len() as isize);
        let x: isize = rng::inclusive_random(self.rooms[r as usize].x + 1, self.rooms[r as usize].x + self.rooms[r as usize].width - 2);
        let y: isize = rng::inclusive_random(self.rooms[r as usize].y + 1, self.rooms[r as usize].y + self.rooms[r as usize].height - 2);

        if self.get_tile(x, y) == Tile::Floor {
            self.set_tile(x, y, tile);

            self.rooms.remove(r as usize);

            return true
        }
        return false;
    }
}

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

    #[test]
    fn test_dungeon() {
        let mut d: Dungeon = Dungeon::new(100, 100);
        let max_features: isize = 78;

        d.generate(max_features);
        // must use cargo test -- --nocapture to see this output
        d._print_dungeon();
    }
}<|endoftext|>


<commit_before>use types::{Result, OscType, OscPacket, OscBundle, OscMessage};
use errors::OscError;
use utils;

use byteorder::{ByteOrder, BigEndian};
use std::iter;

pub fn encode(packet: &OscPacket) -> Result<Vec<u8>> {
    match *packet {
        OscPacket::Message(ref msg) => encode_message(msg),
        OscPacket::Bundle(ref bundle) => encode_bundle(bundle),
    }
}

pub fn encode_message(msg: &OscMessage) -> Result<Vec<u8>> {
    let mut msg_bytes: Vec<u8> = Vec::new();

    msg_bytes.extend(encode_string(&msg.addr));
    let mut type_tags: Vec<char> = vec![','];
    let mut arg_bytes: Vec<u8> = Vec::new();

    if msg.args.is_some() {
        let args = msg.args.as_ref().unwrap();
        for arg in args {
            let (bytes, tag): (Option<Vec<u8>>, char) = try!(encode_arg(arg));

            type_tags.push(tag);
            if bytes.is_some() {
                arg_bytes.extend(bytes.unwrap());
            }
        }
    }

    msg_bytes.extend(encode_string(&type_tags.iter()
                                             .cloned()
                                             .collect::<String>()));
    if arg_bytes.len() > 0 {
        msg_bytes.extend(arg_bytes);
    }
    Ok(msg_bytes)
}

pub fn encode_bundle(bundle: &OscBundle) -> Result<Vec<u8>> {
    let mut bundle_bytes: Vec<u8> = Vec::new();
    bundle_bytes.extend(encode_string(&"#bundle".to_string())
                            .iter()
                            .cloned());

    match try!(encode_arg(&bundle.timetag)) {
        (Some(x), _) => {
            bundle_bytes.extend(x.iter().cloned());
        }
        (None, _) => {
            return Err(OscError::BadBundle("Missing time tag!".to_string()));
        }
    }

    if bundle.content.len() == 0 {
        // TODO: A bundle of length zero, should this really be supported?
        bundle_bytes.extend([0u8; 4].iter().cloned());
        return Ok(bundle_bytes);
    }

    for packet in &bundle.content {
        match packet {
            &OscPacket::Message(ref m) => {
                let msg = try!(encode_message(m));
                let mut msg_size = vec![0u8; 4];
                BigEndian::write_u32(&mut msg_size, msg.len() as u32);
                bundle_bytes.extend(msg_size.iter().chain(msg.iter()).cloned());
            }
            &OscPacket::Bundle(ref b) => {
                let bdl = try!(encode_bundle(b));
                let mut bdl_size = vec![0u8; 4];
                BigEndian::write_u32(&mut bdl_size, bdl.len() as u32);
                bundle_bytes.extend(bdl_size.iter().chain(bdl.iter()).cloned());
            }
        }
    }

    Ok(bundle_bytes)
}

fn encode_arg(arg: &OscType) -> Result<(Option<Vec<u8>>, char)> {
    match arg {
        &OscType::Int(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_i32(&mut bytes, *x);
            Ok((Some(bytes), 'i'))
        }
        &OscType::Long(ref x) => {
            let mut bytes = vec![0u8; 8];
            BigEndian::write_i64(&mut bytes, *x);
            Ok((Some(bytes), 'h'))
        }
        &OscType::Float(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_f32(&mut bytes, *x);
            Ok((Some(bytes), 'f'))
        }
        &OscType::Double(ref x) => {
            let mut bytes = vec![0u8; 8];
            BigEndian::write_f64(&mut bytes, *x);
            Ok((Some(bytes), 'd'))
        }
        &OscType::Char(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_u32(&mut bytes, *x as u32);
            Ok((Some(bytes), 'c'))
        }
        &OscType::String(ref x) => {
            Ok((Some(encode_string(&x)), 's'))
        }
        &OscType::Blob(ref x) => {
            // let mut bytes = x.clone();
            let padded_blob_length: usize = utils::pad(x.len() as u64) as usize;
            let mut bytes = vec![0u8; 4 + padded_blob_length];
            // write length
            BigEndian::write_i32(&mut bytes[..4], x.len() as i32);
            for (i, v) in x.iter().enumerate() {
                bytes[i + 4] = *v;
            }
            Ok((Some(bytes), 'b'))
        }
        &OscType::Time(ref x, ref y) => {
            Ok((Some(encode_time_tag(*x, *y)), 't'))
        }
        &OscType::Midi(ref x) => {
            Ok((Some(vec![x.port, x.status, x.data1, x.data2]), 'm'))
        }
        &OscType::Color(ref x) => {
            Ok((Some(vec![x.red, x.green, x.blue, x.alpha]), 'r'))
        }
        &OscType::Bool(ref x) => {
            if *x {
                Ok((None, 'T'))
            } else {
                Ok((None, 'F'))
            }
        }
        &OscType::Nil => {
            Ok((None, 'N'))
        }
        &OscType::Inf => {
            Ok((None, 'I'))
        }
    }
}

fn encode_string(s: &String) -> Vec<u8> {
    let mut bytes: Vec<u8> = s.as_bytes()
                              .iter()
                              .cloned()
                              .chain(iter::once(0u8))
                              .collect();
    pad_bytes(&mut bytes);
    bytes
}

fn pad_bytes(bytes: &mut Vec<u8>) {
    let padded_lengh = utils::pad(bytes.len() as u64);
    while bytes.len() < padded_lengh as usize {
        bytes.push(0u8)
    }
}

fn encode_time_tag(sec: u32, frac: u32) -> Vec<u8> {
    let mut bytes = vec![0u8; 8];
    BigEndian::write_u32(&mut bytes[..4], sec);
    BigEndian::write_u32(&mut bytes[4..], frac);
    bytes
}
<commit_msg>Leave only `encode()` public<commit_after>use types::{Result, OscType, OscPacket, OscBundle, OscMessage};
use errors::OscError;
use utils;

use byteorder::{ByteOrder, BigEndian};
use std::iter;

pub fn encode(packet: &OscPacket) -> Result<Vec<u8>> {
    match *packet {
        OscPacket::Message(ref msg) => encode_message(msg),
        OscPacket::Bundle(ref bundle) => encode_bundle(bundle),
    }
}

fn encode_message(msg: &OscMessage) -> Result<Vec<u8>> {
    let mut msg_bytes: Vec<u8> = Vec::new();

    msg_bytes.extend(encode_string(&msg.addr));
    let mut type_tags: Vec<char> = vec![','];
    let mut arg_bytes: Vec<u8> = Vec::new();

    if msg.args.is_some() {
        let args = msg.args.as_ref().unwrap();
        for arg in args {
            let (bytes, tag): (Option<Vec<u8>>, char) = try!(encode_arg(arg));

            type_tags.push(tag);
            if bytes.is_some() {
                arg_bytes.extend(bytes.unwrap());
            }
        }
    }

    msg_bytes.extend(encode_string(&type_tags.iter()
                                             .cloned()
                                             .collect::<String>()));
    if arg_bytes.len() > 0 {
        msg_bytes.extend(arg_bytes);
    }
    Ok(msg_bytes)
}

fn encode_bundle(bundle: &OscBundle) -> Result<Vec<u8>> {
    let mut bundle_bytes: Vec<u8> = Vec::new();
    bundle_bytes.extend(encode_string(&"#bundle".to_string())
                            .iter()
                            .cloned());

    match try!(encode_arg(&bundle.timetag)) {
        (Some(x), _) => {
            bundle_bytes.extend(x.iter().cloned());
        }
        (None, _) => {
            return Err(OscError::BadBundle("Missing time tag!".to_string()));
        }
    }

    if bundle.content.len() == 0 {
        // TODO: A bundle of length zero, should this really be supported?
        bundle_bytes.extend([0u8; 4].iter().cloned());
        return Ok(bundle_bytes);
    }

    for packet in &bundle.content {
        match packet {
            &OscPacket::Message(ref m) => {
                let msg = try!(encode_message(m));
                let mut msg_size = vec![0u8; 4];
                BigEndian::write_u32(&mut msg_size, msg.len() as u32);
                bundle_bytes.extend(msg_size.iter().chain(msg.iter()).cloned());
            }
            &OscPacket::Bundle(ref b) => {
                let bdl = try!(encode_bundle(b));
                let mut bdl_size = vec![0u8; 4];
                BigEndian::write_u32(&mut bdl_size, bdl.len() as u32);
                bundle_bytes.extend(bdl_size.iter().chain(bdl.iter()).cloned());
            }
        }
    }

    Ok(bundle_bytes)
}

fn encode_arg(arg: &OscType) -> Result<(Option<Vec<u8>>, char)> {
    match arg {
        &OscType::Int(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_i32(&mut bytes, *x);
            Ok((Some(bytes), 'i'))
        }
        &OscType::Long(ref x) => {
            let mut bytes = vec![0u8; 8];
            BigEndian::write_i64(&mut bytes, *x);
            Ok((Some(bytes), 'h'))
        }
        &OscType::Float(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_f32(&mut bytes, *x);
            Ok((Some(bytes), 'f'))
        }
        &OscType::Double(ref x) => {
            let mut bytes = vec![0u8; 8];
            BigEndian::write_f64(&mut bytes, *x);
            Ok((Some(bytes), 'd'))
        }
        &OscType::Char(ref x) => {
            let mut bytes = vec![0u8; 4];
            BigEndian::write_u32(&mut bytes, *x as u32);
            Ok((Some(bytes), 'c'))
        }
        &OscType::String(ref x) => {
            Ok((Some(encode_string(&x)), 's'))
        }
        &OscType::Blob(ref x) => {
            // let mut bytes = x.clone();
            let padded_blob_length: usize = utils::pad(x.len() as u64) as usize;
            let mut bytes = vec![0u8; 4 + padded_blob_length];
            // write length
            BigEndian::write_i32(&mut bytes[..4], x.len() as i32);
            for (i, v) in x.iter().enumerate() {
                bytes[i + 4] = *v;
            }
            Ok((Some(bytes), 'b'))
        }
        &OscType::Time(ref x, ref y) => {
            Ok((Some(encode_time_tag(*x, *y)), 't'))
        }
        &OscType::Midi(ref x) => {
            Ok((Some(vec![x.port, x.status, x.data1, x.data2]), 'm'))
        }
        &OscType::Color(ref x) => {
            Ok((Some(vec![x.red, x.green, x.blue, x.alpha]), 'r'))
        }
        &OscType::Bool(ref x) => {
            if *x {
                Ok((None, 'T'))
            } else {
                Ok((None, 'F'))
            }
        }
        &OscType::Nil => {
            Ok((None, 'N'))
        }
        &OscType::Inf => {
            Ok((None, 'I'))
        }
    }
}

fn encode_string(s: &String) -> Vec<u8> {
    let mut bytes: Vec<u8> = s.as_bytes()
                              .iter()
                              .cloned()
                              .chain(iter::once(0u8))
                              .collect();
    pad_bytes(&mut bytes);
    bytes
}

fn pad_bytes(bytes: &mut Vec<u8>) {
    let padded_lengh = utils::pad(bytes.len() as u64);
    while bytes.len() < padded_lengh as usize {
        bytes.push(0u8)
    }
}

fn encode_time_tag(sec: u32, frac: u32) -> Vec<u8> {
    let mut bytes = vec![0u8; 8];
    BigEndian::write_u32(&mut bytes[..4], sec);
    BigEndian::write_u32(&mut bytes[4..], frac);
    bytes
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::fmt;
use std::mem;
use std::ops::Deref;
use std::str;

use ArrayVec;
use array::Array;

/// A string with a fixed capacity.
///
/// The `ArrayString` is a string backed by a fixed size array. It keeps track
/// of its length.
///
/// The string is a contiguous value that you can store directly on the stack
/// if needed.
///
/// Due to technical restrictions, this struct does not implement `Copy` even
/// though it would be safe to do so.
pub struct ArrayString<A: Array<Item=u8>> {
    vec: ArrayVec<A>,
}

impl<A: Array<Item=u8>> ArrayString<A> {
    /// Create a new empty `ArrayString`.
    ///
    /// Capacity is inferred from the type parameter.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 16]>::new();
    /// string.push_str("foo");
    /// assert_eq!(&string[..], "foo");
    /// assert_eq!(string.capacity(), 16);
    /// ```
    pub fn new() -> ArrayString<A> {
        ArrayString {
            vec: ArrayVec::new()
        }
    }

    /// Return the capacity of the `ArrayString`.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let string = ArrayString::<[_; 3]>::new();
    /// assert_eq!(string.capacity(), 3);
    /// ```
    #[inline]
    pub fn capacity(&self) -> usize { self.vec.capacity() }

    /// Adds the given character to the end of the string.
    ///
    /// Returns `None` if the push succeeds, or and returns `Some(c)` if the
    /// backing array is not large enough to fit the additional character.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 2]>::new();
    ///
    /// let none1 = string.push('a');
    /// let none2 = string.push('b');
    /// let overflow = string.push('c');
    ///
    /// assert_eq!(&string[..], "ab");
    /// assert_eq!(none1, None);
    /// assert_eq!(none2, None);
    /// assert_eq!(overflow, Some('c'));
    /// ```
    pub fn push(&mut self, c: char) -> Option<char> {
        use std::fmt::Write;
        self.write_char(c).err().map(|_| c)
    }

    /// Adds the given string slice to the end of the string.
    ///
    /// Returns `None` if the push succeeds, or and returns `Some(s)` if the
    /// backing array is not large enough to fit the string.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 2]>::new();
    ///
    /// let none1 = string.push_str("a");
    /// let overflow1 = string.push_str("bc");
    /// let none2 = string.push_str("d");
    /// let overflow2 = string.push_str("ef");
    ///
    /// assert_eq!(&string[..], "ad");
    /// assert_eq!(none1, None);
    /// assert_eq!(none2, None);
    /// assert_eq!(overflow1, Some("bc"));
    /// assert_eq!(overflow2, Some("ef"));
    /// ```
    pub fn push_str<'a>(&mut self, s: &'a str) -> Option<&'a str> {
        if self.len() + s.len() > self.capacity() {
            return Some(s);
        }
        let mut bytes = s.bytes();
        self.vec.extend(&mut bytes);
        assert!(bytes.next().is_none());
        None
    }

    /// Make the string empty.
    pub fn clear(&mut self) {
        mem::replace(self, ArrayString::new());
    }

    /// Set the strings's length.
    ///
    /// May panic if `length` is greater than the capacity.
    ///
    /// This function is `unsafe` because it changes the notion of the
    /// number of “valid” characters in the string. Use with care.
    #[inline]
    pub unsafe fn set_len(&mut self, length: usize) {
        self.vec.set_len(length)
    }
}

impl<A: Array<Item=u8>> Deref for ArrayString<A> {
    type Target = str;
    #[inline]
    fn deref(&self) -> &str {
        unsafe { str::from_utf8_unchecked(&self.vec) }
    }
}

impl<A: Array<Item=u8>> Borrow<str> for ArrayString<A> {
    fn borrow(&self) -> &str { self }
}

impl<A: Array<Item=u8>> AsRef<str> for ArrayString<A> {
    fn as_ref(&self) -> &str { self }
}

impl<A: Array<Item=u8>> fmt::Debug for ArrayString<A> where A::Item: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
}

/// `Write` appends written data to the end of the string.
impl<A: Array<Item=u8>> fmt::Write for ArrayString<A> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        match self.push_str(s) {
            None => Ok(()),
            Some(_) => Err(fmt::Error),
        }
    }
}

//#[derive(Clone, /*Copy,*/ Eq, Hash, Ord, PartialEq, PartialOrd)]
impl<A: Array<Item=u8>> Clone for ArrayString<A> {
    fn clone(&self) -> ArrayString<A> {
        ArrayString { vec: self.vec.clone() }
    }
}

<commit_msg>ArrayString: Make Copy, don't use ArrayVec<commit_after>use std::borrow::Borrow;
use std::fmt;
use std::mem;
use std::ops::Deref;
use std::str;
use std::slice;

use array::Array;
use array::Index;

/// A string with a fixed capacity.
///
/// The `ArrayString` is a string backed by a fixed size array. It keeps track
/// of its length.
///
/// The string is a contiguous value that you can store directly on the stack
/// if needed.
///
/// Due to technical restrictions, this struct does not implement `Copy` even
/// though it would be safe to do so.
#[derive(Copy)]
pub struct ArrayString<A: Array<Item=u8>> {
    xs: A,
    len: A::Index,
}

unsafe fn new_array<A: Array<Item=u8>>() -> A {
    // Note: Returning an uninitialized value here only works
    // if we can be sure the data is never used. The nullable pointer
    // inside enum optimization conflicts with this this for example,
    // so we need to be extra careful. See `NoDrop` enum.
    mem::uninitialized()
}

impl<A: Array<Item=u8>> ArrayString<A> {
    /// Create a new empty `ArrayString`.
    ///
    /// Capacity is inferred from the type parameter.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 16]>::new();
    /// string.push_str("foo");
    /// assert_eq!(&string[..], "foo");
    /// assert_eq!(string.capacity(), 16);
    /// ```
    pub fn new() -> ArrayString<A> {
        unsafe {
            ArrayString {
                xs: new_array(),
                len: Index::from(0),
            }
        }
    }

    /// Return the capacity of the `ArrayString`.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let string = ArrayString::<[_; 3]>::new();
    /// assert_eq!(string.capacity(), 3);
    /// ```
    #[inline]
    pub fn capacity(&self) -> usize { A::capacity() }

    /// Adds the given char to the end of the string.
    ///
    /// Returns `None` if the push succeeds, or and returns `Some(c)` if the
    /// backing array is not large enough to fit the additional char.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 2]>::new();
    ///
    /// let none1 = string.push('a');
    /// let none2 = string.push('b');
    /// let overflow = string.push('c');
    ///
    /// assert_eq!(&string[..], "ab");
    /// assert_eq!(none1, None);
    /// assert_eq!(none2, None);
    /// assert_eq!(overflow, Some('c'));
    /// ```
    pub fn push(&mut self, c: char) -> Option<char> {
        use std::fmt::Write;
        self.write_char(c).err().map(|_| c)
    }

    /// Adds the given string slice to the end of the string.
    ///
    /// Returns `None` if the push succeeds, or and returns `Some(s)` if the
    /// backing array is not large enough to fit the string.
    ///
    /// ```
    /// use arrayvec::ArrayString;
    ///
    /// let mut string = ArrayString::<[_; 2]>::new();
    ///
    /// let none1 = string.push_str("a");
    /// let overflow1 = string.push_str("bc");
    /// let none2 = string.push_str("d");
    /// let overflow2 = string.push_str("ef");
    ///
    /// assert_eq!(&string[..], "ad");
    /// assert_eq!(none1, None);
    /// assert_eq!(none2, None);
    /// assert_eq!(overflow1, Some("bc"));
    /// assert_eq!(overflow2, Some("ef"));
    /// ```
    pub fn push_str<'a>(&mut self, s: &'a str) -> Option<&'a str> {
        use std::io::Write;

        if self.len() + s.len() > self.capacity() {
            return Some(s);
        }
        unsafe {
            let sl = slice::from_raw_parts_mut(self.xs.as_mut_ptr(), A::capacity());
            (&mut sl[self.len()..]).write(s.as_bytes()).unwrap();
            let newl = self.len() + s.len();
            self.set_len(newl);
        }
        None
    }

    /// Make the string empty.
    pub fn clear(&mut self) {
        unsafe {
            self.set_len(0);
        }
    }

    /// Set the strings's length.
    ///
    /// May panic if `length` is greater than the capacity.
    ///
    /// This function is `unsafe` because it changes the notion of the
    /// number of “valid” bytes in the string. Use with care.
    #[inline]
    pub unsafe fn set_len(&mut self, length: usize) {
        debug_assert!(length <= self.capacity());
        self.len = Index::from(length);
    }
}

impl<A: Array<Item=u8>> Deref for ArrayString<A> {
    type Target = str;
    #[inline]
    fn deref(&self) -> &str {
        unsafe {
            let sl = slice::from_raw_parts(self.xs.as_ptr(), self.len.to_usize());
            str::from_utf8_unchecked(sl)
        }
    }
}

impl<A: Array<Item=u8>> Borrow<str> for ArrayString<A> {
    fn borrow(&self) -> &str { self }
}

impl<A: Array<Item=u8>> AsRef<str> for ArrayString<A> {
    fn as_ref(&self) -> &str { self }
}

impl<A: Array<Item=u8>> fmt::Debug for ArrayString<A> where A::Item: fmt::Debug {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { (**self).fmt(f) }
}

/// `Write` appends written data to the end of the string.
impl<A: Array<Item=u8>> fmt::Write for ArrayString<A> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        match self.push_str(s) {
            None => Ok(()),
            Some(_) => Err(fmt::Error),
        }
    }
}

impl<A: Array<Item=u8> + Copy> Clone for ArrayString<A> {
    fn clone(&self) -> ArrayString<A> {
        *self
    }
}

<|endoftext|>


<commit_before>extern crate pkg_config;

use std::io::ErrorKind;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    register_dep("SSH2");
    register_dep("OPENSSL");

    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());
    let target = env::var("TARGET").unwrap();
    let mingw = target.contains("windows-gnu");
    cflags.push_str(" -ffunction-sections -fdata-sections");

    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let src = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
    let dst = PathBuf::from(&env::var("OUT_DIR").unwrap());
    let _ = fs::create_dir(&dst.join("build"));

    let mut cmd = Command::new("cmake");
    cmd.arg(&src.join("libgit2"))
       .current_dir(&dst.join("build"));
    if mingw {
        cmd.arg("-G").arg("Unix Makefiles");
    }
    let profile = match &env::var("PROFILE").unwrap()[..] {
        "bench" | "release" => "Release",
        _ => "Debug",
    };
    run(cmd.arg("-DTHREADSAFE=ON")
           .arg("-DBUILD_SHARED_LIBS=OFF")
           .arg("-DBUILD_CLAR=OFF")
           .arg(&format!("-DCMAKE_BUILD_TYPE={}", profile))
           .arg(&format!("-DCMAKE_INSTALL_PREFIX={}", dst.display()))
           .arg("-DBUILD_EXAMPLES=OFF")
           .arg(&format!("-DCMAKE_C_FLAGS={}", cflags)), "cmake");
    run(Command::new("cmake")
                .arg("--build").arg(".")
                .arg("--target").arg("install")
                .current_dir(&dst.join("build")), "cmake");

    println!("cargo:root={}", dst.display());
    if mingw || target.contains("windows") {
        println!("cargo:rustc-flags=-l winhttp -l rpcrt4 -l ole32 \
                                    -l ws2_32 -l bcrypt -l crypt32 \
                                    -l static=git2");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        return
    }

    if env::var("HOST") == env::var("TARGET") {
        append("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        if pkg_config::Config::new().statik(true).find("libgit2").is_ok() {
            return
        }
    }

    println!("cargo:rustc-flags=-l static=git2");
    println!("cargo:rustc-flags=-L {}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-flags=-l iconv");
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            fail(&format!("failed to execute command: {}\nis `{}` not installed?",
                          e, program));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            append("CMAKE_PREFIX_PATH", PathBuf::from(&s));
            append("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn append(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let val = env::join_paths(env::split_paths(&prefix)
                                  .chain(Some(val).into_iter())).unwrap();
    env::set_var(var, &val);
}

fn fail(s: &str) -> ! {
    panic!("\n{}\n\nbuild script failed, must exit now", s)
}
<commit_msg>Ensure that libgit2 is built with libssh2 enabled<commit_after>extern crate pkg_config;

use std::env;
use std::fs::{self, File};
use std::io::ErrorKind;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(n) => n,
        Err(e) => fail(&format!("\n{} failed with {}\n", stringify!($e), e)),
    })
}

fn main() {
    register_dep("SSH2");
    register_dep("OPENSSL");

    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());
    let target = env::var("TARGET").unwrap();
    let mingw = target.contains("windows-gnu");
    cflags.push_str(" -ffunction-sections -fdata-sections");

    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let src = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
    let dst = PathBuf::from(&env::var("OUT_DIR").unwrap());
    let _ = fs::create_dir(&dst.join("build"));

    let mut cmd = Command::new("cmake");
    cmd.arg(&src.join("libgit2"))
       .current_dir(&dst.join("build"));
    if mingw {
        cmd.arg("-G").arg("Unix Makefiles");
    }
    let profile = match &env::var("PROFILE").unwrap()[..] {
        "bench" | "release" => "Release",
        _ => "Debug",
    };
    run(cmd.arg("-DTHREADSAFE=ON")
           .arg("-DBUILD_SHARED_LIBS=OFF")
           .arg("-DBUILD_CLAR=OFF")
           .arg(&format!("-DCMAKE_BUILD_TYPE={}", profile))
           .arg(&format!("-DCMAKE_INSTALL_PREFIX={}", dst.display()))
           .arg("-DBUILD_EXAMPLES=OFF")
           .arg(&format!("-DCMAKE_C_FLAGS={}", cflags)), "cmake");

    let flags = dst.join("build/CMakeFiles/git2.dir/flags.make");
    let mut contents = String::new();
    t!(t!(File::open(flags)).read_to_string(&mut contents));
    if !contents.contains("-DGIT_SSH") {
        fail("libgit2 failed to find libssh2, and SSH support is required");
    }

    run(Command::new("cmake")
                .arg("--build").arg(".")
                .arg("--target").arg("install")
                .current_dir(&dst.join("build")), "cmake");

    println!("cargo:root={}", dst.display());
    if mingw || target.contains("windows") {
        println!("cargo:rustc-flags=-l winhttp -l rpcrt4 -l ole32 \
                                    -l ws2_32 -l bcrypt -l crypt32 \
                                    -l static=git2");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        return
    }

    if env::var("HOST") == env::var("TARGET") {
        append("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        if pkg_config::Config::new().statik(true).find("libgit2").is_ok() {
            return
        }
    }

    println!("cargo:rustc-flags=-l static=git2");
    println!("cargo:rustc-flags=-L {}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-flags=-l iconv");
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            fail(&format!("failed to execute command: {}\nis `{}` not installed?",
                          e, program));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            append("CMAKE_PREFIX_PATH", PathBuf::from(&s));
            append("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn append(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let val = env::join_paths(env::split_paths(&prefix)
                                  .chain(Some(val).into_iter())).unwrap();
    env::set_var(var, &val);
}

fn fail(s: &str) -> ! {
    panic!("\n{}\n\nbuild script failed, must exit now", s)
}
<|endoftext|>


<commit_before>
#[macro_use] extern crate lazy_static;
extern crate regex;

use regex::Regex;

use std::cmp::Ordering;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;

fn compute_checksum(input: &str) -> String {
    let histogram = input.chars()
        .filter(|&c| c >= 'a' && c <= 'z')
        .fold(HashMap::new(), |mut histogram, c| {
            *histogram.entry(c).or_insert(0) += 1;
            histogram
        });

    // Collect the histogram as a vec of (letter, count) pairs, then sort.
    let mut pairs = histogram.iter().collect::<Vec<_>>();
    pairs.sort_by(|a,b| {
        match b.1.cmp(a.1) { // First by descending count {3,2,1}
            Ordering::Equal => a.0.cmp(b.0), // Then by lexicographic order {a,b,c}
            o => o,
        }
    });

    //println!("{:?}", pairs);

    let checksum = pairs.iter()
        .take(5)
        .map(|&(&letter, _)| letter)
        .fold(String::new(), |mut checksum, c| {checksum.push(c); checksum});

    //println!("Checksum: {:?}", checksum);
    checksum
}

// Returns the sector ID if room is valid, else None.
// Format: "a-b-c-d-e-f-g-h-987[abcde]"
fn verify_checksum(input: &str) -> Option<i32> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r##"([-a-z]+)([:digit:]+)\[([:alpha:]+)\]"##).unwrap();
    }
    let cap = RE.captures(input).unwrap();
    let name = cap.at(1).unwrap();
    let sector_id: i32 = cap.at(2).unwrap().parse().unwrap();
    let stored_checksum = cap.at(3).unwrap();

    //println!("{} | {} | {}", name, sector_id, stored_checksum);

    let checksum = compute_checksum(name);

    if checksum == stored_checksum {Some(sector_id)} else {None}
}

fn main() {
    let mut input_string = String::new();
    let mut file = File::open("input.txt").unwrap();
    let _ = file.read_to_string(&mut input_string);

    // Accumulate the sector ids of all correct rooms.
    let sum = input_string.lines()
        .map(verify_checksum)
        .filter_map(|x| x)
        .fold(0, |acc, id| acc + id);

    println!("Part 1: sum of sector IDs = {:?}", sum);
}

#[test]
fn test1() {
    assert!(compute_checksum("aaaaa-bbb-z-y-x-123[abxyz]") == "abxyz");
    assert!(compute_checksum("a-b-c-d-e-f-g-h-987[abcde]") == "abcde");
    assert!(compute_checksum("not-a-real-room-404[oarel]") == "oarel");
    assert!(compute_checksum("totally-real-room-200[decoy]") != "decoy");

    assert!(verify_checksum("aaaaa-bbb-z-y-x-123[abxyz]") == Some(123));
    assert!(verify_checksum("a-b-c-d-e-f-g-h-987[abcde]") == Some(987));
    assert!(verify_checksum("not-a-real-room-404[oarel]") == Some(404));
    assert!(verify_checksum("totally-real-room-200[decoy]") == None);
}<commit_msg>Day 4, part 2<commit_after>
#[macro_use] extern crate lazy_static;
extern crate regex;

use regex::Regex;

use std::cmp::Ordering;
use std::fs::File;
use std::io::Read;
use std::collections::HashMap;

// Returns (name, sector_id, stored checksum)
fn unpack(input: &str) -> (&str, i32, &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r##"([-a-z]+)-([:digit:]+)\[([:alpha:]+)\]"##).unwrap();
    }

    let cap = RE.captures(input).unwrap();
    let name = cap.at(1).unwrap();
    let sector_id: i32 = cap.at(2).unwrap().parse().unwrap();
    let checksum = cap.at(3).unwrap();

    (name, sector_id, checksum)
}

fn compute_checksum(input: &str) -> String {
    let histogram = input.chars()
        .filter(|&c| c >= 'a' && c <= 'z')
        .fold(HashMap::new(), |mut histogram, c| {
            *histogram.entry(c).or_insert(0) += 1;
            histogram
        });

    // Collect the histogram as a vec of (letter, count) pairs, then sort.
    let mut pairs = histogram.iter().collect::<Vec<_>>();
    pairs.sort_by(|a,b| {
        match b.1.cmp(a.1) { // First by descending count {3,2,1}
            Ordering::Equal => a.0.cmp(b.0), // Then by lexicographic order {a,b,c}
            o => o,
        }
    });

    pairs.iter()
        .take(5)
        .map(|&(&letter, _)| letter)
        .fold(String::new(), |mut checksum, c| {checksum.push(c); checksum})
}

// Returns the sector ID if room is valid, else None.
// Format: "a-b-c-d-e-f-g-h-987[abcde]"
fn verify_checksum(input: &str) -> Option<i32> {
    let (name, sector_id, stored_checksum) = unpack(input);
    let checksum = compute_checksum(name);
    if checksum == stored_checksum {Some(sector_id)} else {None}
}

fn shift_char(c: char, shift: i32) -> char {
    let code = (((c as i32 - 'a' as i32 + shift) % 26) + 'a' as i32) as u32;
    std::char::from_u32(code).unwrap()
}

fn decrypt(input: &str) -> (String, i32) {
    let (name, shift, _) = unpack(input);
    let name = name.chars()
        .map(|c| if c == '-' {' '} else {shift_char(c, shift)})
        .fold(String::new(), |mut string, c| {string.push(c); string});
    (name, shift)
}

fn main() {
    let mut input_string = String::new();
    let mut file = File::open("input.txt").unwrap();
    let _ = file.read_to_string(&mut input_string);

    // Accumulate the sector ids of all correct rooms.
    let sum = input_string.lines()
        .map(verify_checksum)
        .filter_map(|x| x)
        .fold(0, |acc, id| acc + id);

    println!("Part 1: sum of valid sector IDs = {:?}", sum);
    assert!(sum == 185371);

    // Part 2: decrypt names
    let decrypted_names = input_string.lines()
        .filter(|val| verify_checksum(val).is_some())
        .map(decrypt)
        .filter(|&(ref string, _)| string.contains("north"))
        .collect::<Vec<_>>();
    for val in &decrypted_names {
        println!("{:?}", val);
    }
    assert!(decrypted_names[0].1 == 984);
}

#[test]
fn test1() {
    assert!(compute_checksum("aaaaa-bbb-z-y-x-123[abxyz]") == "abxyz");
    assert!(compute_checksum("a-b-c-d-e-f-g-h-987[abcde]") == "abcde");
    assert!(compute_checksum("not-a-real-room-404[oarel]") == "oarel");
    assert!(compute_checksum("totally-real-room-200[decoy]") != "decoy");

    assert!(verify_checksum("aaaaa-bbb-z-y-x-123[abxyz]") == Some(123));
    assert!(verify_checksum("a-b-c-d-e-f-g-h-987[abcde]") == Some(987));
    assert!(verify_checksum("not-a-real-room-404[oarel]") == Some(404));
    assert!(verify_checksum("totally-real-room-200[decoy]") == None);
}

#[test]
fn decryption_test() {
    assert!(decrypt("qzmt-zixmtkozy-ivhz-343[blank]") == "very encrypted name");
}<|endoftext|>


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

//!
//! Protect the server's long-term key with envelope encryption and a key management system.
//!
//! Note: KMS support must be enabled at compile time, see the Roughenough's [documentation
//! on optional features](https://github.com/int08h/roughenough/blob/master/doc/OPTIONAL-FEATURES.md#key-management-system-kms-support)
//! for instructions.
//!
//! ## Motivation
//!
//! The seed for the server's [long-term key](../key/struct.LongTermKey.html) is subject to
//! contradictory requirements:
//!
//!   1. The seed must be kept secret, but
//!   2. The seed must be available at server start-up to create the
//!      [delegated on-line key](../key/struct.OnlineKey.html)
//!
//! ## Plaintext seed
//!
//! The default option is to store the seed in plaintext as part of the server's configuration.
//! This usually means the seed is present in the clear: on disk, in a repository, or otherwise
//! durably persisted where it can be compromised (accidentally or maliciously).
//!
//! ## Encrypting the seed
//!
//! Envelope encryption protects the seed by encrypting it with a locally generated 256-bit
//! Data Encryption Key (DEK). The DEK itself is then encrypted using a cloud key management
//! system (KMS). The resulting opaque encrypted "blob" (encrypted seed + encrypted DEK) is
//! stored in the Roughenough configuration.
//!
//! At server start-up the KMS is used to decrypt the DEK, which is then used to (in memory)
//! decrypt the seed. The seed is used to generate the
//! [delegated on-line key](../key/struct.OnlineKey.html) after which the seed and DEK are erased
//! from memory.
//!
//! See
//!   * [`EnvelopeEncryption`](struct.EnvelopeEncryption.html) for Roughenough's implementation.
//!   * [Google](https://cloud.google.com/kms/docs/envelope-encryption) or
//!     [Amazon](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#enveloping)
//!     for more in-depth explanations of envelope encryption.
//!

mod envelope;

use base64;
use ring;
use std;

use crate::config::ServerConfig;
use crate::error;
use crate::key::KmsProtection;

pub use self::envelope::EnvelopeEncryption;

/// Errors generated by KMS operations
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone)]
pub enum KmsError {
    OperationFailed(String),
    InvalidConfiguration(String),
    InvalidData(String),
    InvalidKey(String),
}

impl From<std::io::Error> for KmsError {
    fn from(error: std::io::Error) -> Self {
        KmsError::OperationFailed(format!("{:?}", error))
    }
}

impl From<ring::error::Unspecified> for KmsError {
    fn from(_: ring::error::Unspecified) -> Self {
        KmsError::OperationFailed("unspecified ring cryptographic failure".to_string())
    }
}

impl From<base64::DecodeError> for KmsError {
    fn from(error: base64::DecodeError) -> Self {
        KmsError::OperationFailed(format!("base64: {}", error))
    }
}

// Size of the AEAD nonce in bytes.
const NONCE_SIZE_BYTES: usize = 12;

// Size of the AEAD authentication tag in bytes.
const TAG_SIZE_BYTES: usize = 16;

// Size of the 256-bit Data Encryption Key (DEK) in bytes.
const DEK_SIZE_BYTES: usize = 32;

// Trivial domain separation to guard against KMS key reuse
const AD: &str = "roughenough";

/// An unencrypted (plaintext) 256-bit Data Encryption Key (DEK).
pub type PlaintextDEK = Vec<u8>;

/// A Data Encryption Key (DEK) that has been encrypted (wrapped) by a Key Management System (KMS).
///
/// This is an opaque, implementation-specific value. AEAD tag size, nonce size,
/// provider metadata, and so on will vary between [`KmsProvider`](trait.KmsProvider.html)
/// implementations.
pub type EncryptedDEK = Vec<u8>;

///
/// A key management system that wraps/unwraps a data encryption key (DEK).
///
pub trait KmsProvider {
    /// Make a blocking request to encrypt (wrap) the provided plaintext data encryption key.
    fn encrypt_dek(&self, plaintext_dek: &PlaintextDEK) -> Result<EncryptedDEK, KmsError>;

    /// Make a blocking request to decrypt (unwrap) a previously encrypted data encryption key.
    fn decrypt_dek(&self, encrypted_dek: &EncryptedDEK) -> Result<PlaintextDEK, KmsError>;
}

#[cfg(feature = "awskms")]
mod awskms;

#[cfg(feature = "awskms")]
pub use crate::kms::awskms::inner::AwsKms;

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.kms_protection()`:
///
///  * If `config.kms_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.kms_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(feature = "awskms")]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    use crate::kms::envelope::EnvelopeEncryption;

    match config.kms_protection() {
        KmsProtection::Plaintext => Ok(config.seed()),
        KmsProtection::AwsKmsEnvelope(key_id) => {
            info!("Unwrapping seed via AWS KMS key '{}'", key_id);
            let kms = AwsKms::from_arn(key_id)?;
            let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
            Ok(seed)
        }
        _ => Err(error::Error::InvalidConfiguration(
            "Google KMS not supported".to_string(),
        )),
    }
}

#[cfg(feature = "gcpkms")]
mod gcpkms;

#[cfg(feature = "gcpkms")]
pub use crate::kms::gcpkms::inner::GcpKms;

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.kms_protection()`:
///
///  * If `config.kms_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.kms_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(feature = "gcpkms")]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    use crate::kms::envelope::EnvelopeEncryption;

    match config.kms_protection() {
        KmsProtection::Plaintext => Ok(config.seed()),
        KmsProtection::GoogleKmsEnvelope(resource_id) => {
            info!("Unwrapping seed via Google KMS key '{}'", resource_id);
            let kms = GcpKms::from_resource_id(resource_id)?;
            let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
            Ok(seed)
        }
        _ => Err(error::Error::InvalidConfiguration(
            "AWS KMS not supported".to_string(),
        )),
    }
}

/// Load the seed value for the long-term key.
///
/// ## This build has KMS disabled
///
/// *The KMS feature is disabled in this build of Roughenough*.
///
/// The only supported `kms_protection` value in this build is `plaintext`. Any
/// other value will cause a runtime error.
///
/// ## Background
///
/// Loading behavior depends on the value of `config.kms_protection()`:
///
///  * If `config.kms_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.kms_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(not(any(feature = "awskms", feature = "gcpkms")))]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    match config.kms_protection() {
        KmsProtection::Plaintext => Ok(config.seed()),
        v => Err(error::Error::InvalidConfiguration(format!(
            "kms_protection '{}' requires KMS, but server was not compiled with KMS support",
            v
        ))),
    }
}
<commit_msg>Remove redundant imports of EnvelopeEncryption<commit_after>// Copyright 2017-2019 int08h LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//!
//! Protect the server's long-term key with envelope encryption and a key management system.
//!
//! Note: KMS support must be enabled at compile time, see the Roughenough's [documentation
//! on optional features](https://github.com/int08h/roughenough/blob/master/doc/OPTIONAL-FEATURES.md#key-management-system-kms-support)
//! for instructions.
//!
//! ## Motivation
//!
//! The seed for the server's [long-term key](../key/struct.LongTermKey.html) is subject to
//! contradictory requirements:
//!
//!   1. The seed must be kept secret, but
//!   2. The seed must be available at server start-up to create the
//!      [delegated on-line key](../key/struct.OnlineKey.html)
//!
//! ## Plaintext seed
//!
//! The default option is to store the seed in plaintext as part of the server's configuration.
//! This usually means the seed is present in the clear: on disk, in a repository, or otherwise
//! durably persisted where it can be compromised (accidentally or maliciously).
//!
//! ## Encrypting the seed
//!
//! Envelope encryption protects the seed by encrypting it with a locally generated 256-bit
//! Data Encryption Key (DEK). The DEK itself is then encrypted using a cloud key management
//! system (KMS). The resulting opaque encrypted "blob" (encrypted seed + encrypted DEK) is
//! stored in the Roughenough configuration.
//!
//! At server start-up the KMS is used to decrypt the DEK, which is then used to (in memory)
//! decrypt the seed. The seed is used to generate the
//! [delegated on-line key](../key/struct.OnlineKey.html) after which the seed and DEK are erased
//! from memory.
//!
//! See
//!   * [`EnvelopeEncryption`](struct.EnvelopeEncryption.html) for Roughenough's implementation.
//!   * [Google](https://cloud.google.com/kms/docs/envelope-encryption) or
//!     [Amazon](https://docs.aws.amazon.com/kms/latest/developerguide/concepts.html#enveloping)
//!     for more in-depth explanations of envelope encryption.
//!

mod envelope;

use base64;
use ring;
use std;

use crate::config::ServerConfig;
use crate::error;
use crate::key::KmsProtection;

pub use self::envelope::EnvelopeEncryption;

/// Errors generated by KMS operations
#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone)]
pub enum KmsError {
    OperationFailed(String),
    InvalidConfiguration(String),
    InvalidData(String),
    InvalidKey(String),
}

impl From<std::io::Error> for KmsError {
    fn from(error: std::io::Error) -> Self {
        KmsError::OperationFailed(format!("{:?}", error))
    }
}

impl From<ring::error::Unspecified> for KmsError {
    fn from(_: ring::error::Unspecified) -> Self {
        KmsError::OperationFailed("unspecified ring cryptographic failure".to_string())
    }
}

impl From<base64::DecodeError> for KmsError {
    fn from(error: base64::DecodeError) -> Self {
        KmsError::OperationFailed(format!("base64: {}", error))
    }
}

// Size of the AEAD nonce in bytes.
const NONCE_SIZE_BYTES: usize = 12;

// Size of the AEAD authentication tag in bytes.
const TAG_SIZE_BYTES: usize = 16;

// Size of the 256-bit Data Encryption Key (DEK) in bytes.
const DEK_SIZE_BYTES: usize = 32;

// Trivial domain separation to guard against KMS key reuse
const AD: &str = "roughenough";

/// An unencrypted (plaintext) 256-bit Data Encryption Key (DEK).
pub type PlaintextDEK = Vec<u8>;

/// A Data Encryption Key (DEK) that has been encrypted (wrapped) by a Key Management System (KMS).
///
/// This is an opaque, implementation-specific value. AEAD tag size, nonce size,
/// provider metadata, and so on will vary between [`KmsProvider`](trait.KmsProvider.html)
/// implementations.
pub type EncryptedDEK = Vec<u8>;

///
/// A key management system that wraps/unwraps a data encryption key (DEK).
///
pub trait KmsProvider {
    /// Make a blocking request to encrypt (wrap) the provided plaintext data encryption key.
    fn encrypt_dek(&self, plaintext_dek: &PlaintextDEK) -> Result<EncryptedDEK, KmsError>;

    /// Make a blocking request to decrypt (unwrap) a previously encrypted data encryption key.
    fn decrypt_dek(&self, encrypted_dek: &EncryptedDEK) -> Result<PlaintextDEK, KmsError>;
}

#[cfg(feature = "awskms")]
mod awskms;

#[cfg(feature = "awskms")]
pub use crate::kms::awskms::inner::AwsKms;

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.kms_protection()`:
///
///  * If `config.kms_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.kms_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(feature = "awskms")]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    match config.kms_protection() {
        KmsProtection::Plaintext => Ok(config.seed()),
        KmsProtection::AwsKmsEnvelope(key_id) => {
            info!("Unwrapping seed via AWS KMS key '{}'", key_id);
            let kms = AwsKms::from_arn(key_id)?;
            let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
            Ok(seed)
        }
        _ => Err(error::Error::InvalidConfiguration(
            "Google KMS not supported".to_string(),
        )),
    }
}

#[cfg(feature = "gcpkms")]
mod gcpkms;

#[cfg(feature = "gcpkms")]
pub use crate::kms::gcpkms::inner::GcpKms;

/// Load the seed value for the long-term key.
///
/// Loading behavior depends on the value of `config.kms_protection()`:
///
///  * If `config.kms_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.kms_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(feature = "gcpkms")]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    match config.kms_protection() {
        KmsProtection::Plaintext => Ok(config.seed()),
        KmsProtection::GoogleKmsEnvelope(resource_id) => {
            info!("Unwrapping seed via Google KMS key '{}'", resource_id);
            let kms = GcpKms::from_resource_id(resource_id)?;
            let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
            Ok(seed)
        }
        _ => Err(error::Error::InvalidConfiguration(
            "AWS KMS not supported".to_string(),
        )),
    }
}

/// Load the seed value for the long-term key.
///
/// ## This build has KMS disabled
///
/// *The KMS feature is disabled in this build of Roughenough*.
///
/// The only supported `kms_protection` value in this build is `plaintext`. Any
/// other value will cause a runtime error.
///
/// ## Background
///
/// Loading behavior depends on the value of `config.kms_protection()`:
///
///  * If `config.kms_protection() == Plaintext` then the value returned from `config.seed()`
///    is used as-is and assumed to be a 32-byte hexadecimal value.
///
///  * Otherwise `config.seed()` is assumed to be an encrypted opaque blob generated from
///    a prior `EnvelopeEncryption::encrypt_seed` call. The value of `config.kms_protection()`
///    is parsed as a KMS key id and `EnvelopeEncryption::decrypt_seed` is called to obtain
///    the plaintext seed value.
///
#[cfg(not(any(feature = "awskms", feature = "gcpkms")))]
pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
    match config.kms_protection() {
        KmsProtection::Plaintext => Ok(config.seed()),
        v => Err(error::Error::InvalidConfiguration(format!(
            "kms_protection '{}' requires KMS, but server was not compiled with KMS support",
            v
        ))),
    }
}
<|endoftext|>