<commit_before>#![macro_use]

// For testing purposes, we want to generate valid `Ast`s,
//  but `ast!` is clunky and makes it *super* easy to leave off `Quote*` and `ExtendEnv`
//  and actually running the parser is a big dependency (and requires huge string literals).
//  so we introduce a `u!` macro that looks up forms but infers the internal structure.

// It's weird to be relying on the grammar while ignoring parts of it, hence "flimsy",
//  but errors are much more likely to be compile-time than inscrutable test problems.

// It's not unsafe to use `u!` for runtime operations, but there's a runtime cost, so don't do it.

use crate::{
    ast::Ast::{self, *},
    grammar::FormPat,
    name::*,
    util::mbe::EnvMBE,
};
use std::iter::{Iterator, Peekable};

// First, transforms from `[a b c; d e f; g h i]` to `[g h i] {[a b c] [d e f]}`
//   to get around a Rust macro parsing restriction,
//  then makes a REP flimsy shape for it.
macro_rules! u_rep {
    ([; $( $ts:tt )*]   $acc_cur:tt { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [] {  $( $acc_rest )*  $acc_cur })
    };
    ([$t:tt $( $ts:tt )*]  [ $( $acc_cur:tt )* ] { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [$($acc_cur)* $t] { $( $acc_rest )* })
    };
    ([] [] {}) => {
        // Empty repeat
        crate::ast::Shape(vec![crate::ast::Atom(n("REP"))])
    };
    ([]  [ $( $acc_cur:tt )* ] { $( [ $( $acc_rest:tt )* ] )* }) => {
        crate::ast::Shape(vec![
            crate::ast::Atom(n("REP")),
            $( u_shape_if_many!(  $($acc_rest)* ), )*
            u_shape_if_many!(  $($acc_cur)* )
        ])
    };
}

macro_rules! u_shape_if_many {
    ($t:tt) => {
        u!($t)
    };
    () => {
        compile_error!("empty u!")
    };
    ( $($ts:tt)* ) => {
        u!((~ $($ts)* ))
    };
}

thread_local! {
    pub static default_nt: std::cell::RefCell<String> = std::cell::RefCell::new("Expr".to_owned());
}

macro_rules! u {
    ($atom:ident) => {
        // Default to this, because `Call` will use whatever it's given, without a grammar:
        crate::ast::VariableReference(n(stringify!($atom)))
    };
    ( [ , $seq:expr ] ) => {
        {
            let mut contents: Vec<Ast> = $seq;
            contents.insert(0, crate::ast::Atom(n("REP")));
            crate::ast::Shape(contents)
        }
    };
    ( [ $( $ts:tt )*  ] ) => {
        u_rep!( [$($ts)*] [] {} )
    };
    ( { $form:ident : $( $ts:tt )*} ) => {
        {
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                crate::core_forms::find_core_form(&def_nt.borrow(), stringify!($form))
            });
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing)
        }
    };
    ( { $nt:ident $form:ident : $( $ts:tt )*} ) => {
        {
            let mut old_default_nt = "".to_owned();
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                let nt = stringify!($nt);
                *def_nt.borrow_mut() = nt.to_string();

                crate::core_forms::find_core_form(&nt, stringify!($form))
            });
            let res = crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing);
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
    // The need for explicit exports is unfortunate;
    //  that information is part of `Scope`, not `Form` (maybe we should change that?)
    ( { $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                crate::core_forms::find_core_form(&def_nt.borrow(), stringify!($form))
            });
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                ebeta!($ebeta))
        }
    };
    ( { $nt:ident $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            // code duplication from above ) :
            let mut old_default_nt = "".to_owned();
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                let nt = stringify!($nt);
                *def_nt.borrow_mut() = nt.to_string();

                crate::core_forms::find_core_form(&nt, stringify!($form))
            });

            let res =crate::ast::Node(f.clone(),
                    crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                        .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                    ebeta!($ebeta));
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
    ( { $form:expr ; $( $ts:tt )*} ) => {
        {
            let f = $form;
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing)
        }
    };
    ({ $( $anything:tt )* }) => {
        compile_error!("Needed a : or ; in u!");
    };
    // Currently, nested `Seq`s need to correspond to nested `SEQ`s, so this creates one explicitly:
    ((~ $($ts:tt)*)) => {
        crate::ast::Shape(vec![
            crate::ast::Atom(n("SEQ")),
            $( u!( $ts ) ),*
        ])
    };
    ((at $t:tt)) => {
        crate::ast::Atom(n(stringify!($t)))
    };
    ((prim $t:tt)) => {
        crate::core_type_forms::get__primitive_type(n(stringify!($t)))
    };
    ((, $interpolate:expr)) => {
        $interpolate
    };
    // Two or more token trees (avoid infinite regress by not handling the one-element case)
    ( $t_first:tt $t_second:tt $( $t:tt )* ) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("SEQ")),
            u!( $t_first ), u!( $t_second ), $( u!( $t ) ),*
        ])
    };
}

macro_rules! uty {
    ($( $ts:tt )*) => {
        {
            let mut old_default_nt = "".to_owned();
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                *def_nt.borrow_mut() = "Type".to_owned();
            });
            let res = u!( $($ts)* );

            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
}

fn parse_flimsy_seq<'a, I>(flimsy_seq: &mut Peekable<I>, grammar: &FormPat) -> EnvMBE<Ast>
where I: Iterator<Item = &'a Ast> {
    use crate::grammar::FormPat::*;

    match grammar {
        Seq(ref grammar_parts) => {
            let mut result = EnvMBE::new();

            for grammar_part in grammar_parts {
                result = result.combine_overriding(&parse_flimsy_seq(flimsy_seq, grammar_part));
            }
            result
        }
        _ => {
            let flimsy = *match flimsy_seq.peek() {
                None => return EnvMBE::new(), // Or is this an error?
                Some(f) => f,
            };
            match parse_flimsy_mbe(flimsy, grammar) {
                None => EnvMBE::new(),
                Some(res) => {
                    // `Anyways`es shouldn't consume anything (and they'll always be `Named`):
                    let consuming = match grammar {
                        Named(_, ref body) => match **body {
                            Anyways(_) => false,
                            _ => true,
                        },
                        _ => true,
                    };
                    if consuming {
                        let _ = flimsy_seq.next();
                    }
                    res
                }
            }
        }
    }
}

pub fn parse_flimsy_mbe(flimsy: &Ast, grammar: &FormPat) -> Option<EnvMBE<Ast>> {
    use crate::grammar::FormPat::*;

    match grammar {
        Literal(_, _) => None,
        Call(_) => None,
        Scan(_) => None,
        Seq(_) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("SEQ")) {
                    panic!("Needed a SEQ, got {}", flimsy)
                }
                let mut fpi = flimsy_parts[1..].iter().peekable();

                Some(parse_flimsy_seq(&mut fpi, grammar))
            }
            _ => panic!("Needed a SEQ shape, got {}", flimsy),
        },
        Star(ref body) | Plus(ref body) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("REP")) {
                    panic!("Need a REP, got {}", flimsy_parts[0])
                }

                let mut reps = vec![];
                for flimsy_part in flimsy_parts[1..].iter() {
                    reps.push(parse_flimsy_mbe(flimsy_part, &*body).unwrap());
                }
                Some(EnvMBE::new_from_anon_repeat(reps))
            }
            _ => panic!("Needed a REP shape, got {}", flimsy),
        },
        Alt(ref subs) => {
            // HACK: always pick the first branch of the `Alt`
            // (mainly affects unquotation, where it skips the type annotation)
            parse_flimsy_mbe(flimsy, &*subs[0])
        }
        Named(name, ref body) => Some(EnvMBE::new_from_leaves(
            crate::util::assoc::Assoc::new().set(*name, parse_flimsy_ast(flimsy, &*body)),
        )),
        SynImport(_, _, _) => panic!("`SynImport` can't work without a real parser"),
        NameImport(_, _) => panic!("`NameImport` should live underneath `Named`: {:?}", grammar),
        _ => unimplemented!("Can't handle {:?}", grammar),
    }
}

fn parse_flimsy_ast(flimsy: &Ast, grammar: &FormPat) -> Ast {
    use crate::grammar::FormPat::*;

    match grammar {
        Anyways(ref a) => a.clone(),
        Impossible => unimplemented!(),
        Scan(_) => flimsy.clone(),
        Literal(_, _) => Trivial,
        VarRef(_) => match flimsy {
            VariableReference(a) => VariableReference(*a),
            non_atom => panic!("Needed an atom, got {}", non_atom),
        },
        NameImport(body, beta) => {
            ExtendEnv(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        QuoteDeepen(body, pos) => QuoteMore(Box::new(parse_flimsy_ast(flimsy, &*body)), *pos),
        QuoteEscape(body, depth) => QuoteLess(Box::new(parse_flimsy_ast(flimsy, &*body)), *depth),

        Call(name) => {
            // HACK: don't descend into `Call(n("DefaultAtom"))
            if *name == n("DefaultAtom") || *name == n("AtomNotInPat") {
                match flimsy {
                    VariableReference(a) => Atom(*a),
                    non_atom => panic!("Needed an atom, got {}", non_atom),
                }
            } else {
                flimsy.clone()
            }
        }
        _ => unimplemented!("Can't handle {:?}", grammar),
    }
}
<commit_msg>Update flimsy_syntax to support phaselessness.<commit_after>#![macro_use]

// For testing purposes, we want to generate valid `Ast`s,
//  but `ast!` is clunky and makes it *super* easy to leave off `Quote*` and `ExtendEnv`
//  and actually running the parser is a big dependency (and requires huge string literals).
//  so we introduce a `u!` macro that looks up forms but infers the internal structure.

// It's weird to be relying on the grammar while ignoring parts of it, hence "flimsy",
//  but errors are much more likely to be compile-time than inscrutable test problems.

// It's not unsafe to use `u!` for runtime operations, but there's a runtime cost, so don't do it.

use crate::{
    ast::Ast::{self, *},
    grammar::FormPat,
    name::*,
    util::mbe::EnvMBE,
};
use std::iter::{Iterator, Peekable};

// First, transforms from `[a b c; d e f; g h i]` to `[g h i] {[a b c] [d e f]}`
//   to get around a Rust macro parsing restriction,
//  then makes a REP flimsy shape for it.
macro_rules! u_rep {
    ([; $( $ts:tt )*]   $acc_cur:tt { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [] {  $( $acc_rest )*  $acc_cur })
    };
    ([$t:tt $( $ts:tt )*]  [ $( $acc_cur:tt )* ] { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [$($acc_cur)* $t] { $( $acc_rest )* })
    };
    ([] [] {}) => {
        // Empty repeat
        crate::ast::Shape(vec![crate::ast::Atom(n("REP"))])
    };
    ([]  [ $( $acc_cur:tt )* ] { $( [ $( $acc_rest:tt )* ] )* }) => {
        crate::ast::Shape(vec![
            crate::ast::Atom(n("REP")),
            $( u_shape_if_many!(  $($acc_rest)* ), )*
            u_shape_if_many!(  $($acc_cur)* )
        ])
    };
}

macro_rules! u_shape_if_many {
    ($t:tt) => {
        u!($t)
    };
    () => {
        compile_error!("empty u!")
    };
    ( $($ts:tt)* ) => {
        u!((~ $($ts)* ))
    };
}

thread_local! {
    pub static default_nt: std::cell::RefCell<String> = std::cell::RefCell::new("Expr".to_owned());
}

macro_rules! u {
    ($atom:ident) => {
        // Default to this, because `Call` will use whatever it's given, without a grammar:
        crate::ast::VariableReference(n(stringify!($atom)))
    };
    ( [ , $seq:expr ] ) => {
        {
            let mut contents: Vec<Ast> = $seq;
            contents.insert(0, crate::ast::Atom(n("REP")));
            crate::ast::Shape(contents)
        }
    };
    ( [ $( $ts:tt )*  ] ) => {
        u_rep!( [$($ts)*] [] {} )
    };
    ( { $form:ident : $( $ts:tt )*} ) => {
        {
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                crate::core_forms::find_core_form(&def_nt.borrow(), stringify!($form))
            });
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing)
        }
    };
    ( { $nt:ident $form:ident : $( $ts:tt )*} ) => {
        {
            let mut old_default_nt = "".to_owned();
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                let nt = stringify!($nt);
                *def_nt.borrow_mut() = nt.to_string();

                crate::core_forms::find_core_form(&nt, stringify!($form))
            });
            let res = crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing);
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
    // The need for explicit exports is unfortunate;
    //  that information is part of `Scope`, not `Form` (maybe we should change that?)
    ( { $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                crate::core_forms::find_core_form(&def_nt.borrow(), stringify!($form))
            });
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                ebeta!($ebeta))
        }
    };
    ( { $nt:ident $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            // code duplication from above ) :
            let mut old_default_nt = "".to_owned();
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                let nt = stringify!($nt);
                *def_nt.borrow_mut() = nt.to_string();

                crate::core_forms::find_core_form(&nt, stringify!($form))
            });

            let res =crate::ast::Node(f.clone(),
                    crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                        .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                    ebeta!($ebeta));
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
    ( { $form:expr ; $( $ts:tt )*} ) => {
        {
            let f = $form;
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing)
        }
    };
    ({ $( $anything:tt )* }) => {
        compile_error!("Needed a : or ; in u!");
    };
    // Currently, nested `Seq`s need to correspond to nested `SEQ`s, so this creates one explicitly:
    ((~ $($ts:tt)*)) => {
        crate::ast::Shape(vec![
            crate::ast::Atom(n("SEQ")),
            $( u!( $ts ) ),*
        ])
    };
    ((at $t:tt)) => {
        crate::ast::Atom(n(stringify!($t)))
    };
    ((prim $t:tt)) => {
        crate::core_type_forms::get__primitive_type(n(stringify!($t)))
    };
    ((, $interpolate:expr)) => {
        $interpolate
    };
    // Two or more token trees (avoid infinite regress by not handling the one-element case)
    ( $t_first:tt $t_second:tt $( $t:tt )* ) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("SEQ")),
            u!( $t_first ), u!( $t_second ), $( u!( $t ) ),*
        ])
    };
}

macro_rules! uty {
    ($( $ts:tt )*) => {
        {
            let mut old_default_nt = "".to_owned();
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                *def_nt.borrow_mut() = "Type".to_owned();
            });
            let res = u!( $($ts)* );

            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
}

fn parse_flimsy_seq<'a, I>(flimsy_seq: &mut Peekable<I>, grammar: &FormPat) -> EnvMBE<Ast>
where I: Iterator<Item = &'a Ast> {
    use crate::grammar::FormPat::*;

    match grammar {
        Seq(ref grammar_parts) => {
            let mut result = EnvMBE::new();

            for grammar_part in grammar_parts {
                result = result.combine_overriding(&parse_flimsy_seq(flimsy_seq, grammar_part));
            }
            result
        }
        _ => {
            let flimsy = *match flimsy_seq.peek() {
                None => return EnvMBE::new(), // Or is this an error?
                Some(f) => f,
            };
            match parse_flimsy_mbe(flimsy, grammar) {
                None => EnvMBE::new(),
                Some(res) => {
                    // `Anyways`es shouldn't consume anything (and they'll always be `Named`):
                    let consuming = match grammar {
                        Named(_, ref body) => match **body {
                            Anyways(_) => false,
                            _ => true,
                        },
                        _ => true,
                    };
                    if consuming {
                        let _ = flimsy_seq.next();
                    }
                    res
                }
            }
        }
    }
}

pub fn parse_flimsy_mbe(flimsy: &Ast, grammar: &FormPat) -> Option<EnvMBE<Ast>> {
    use crate::grammar::FormPat::*;

    match grammar {
        Literal(_, _) => None,
        Call(_) => None,
        Scan(_) => None,
        Seq(_) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("SEQ")) {
                    panic!("Needed a SEQ, got {}", flimsy)
                }
                let mut fpi = flimsy_parts[1..].iter().peekable();

                Some(parse_flimsy_seq(&mut fpi, grammar))
            }
            _ => panic!("Needed a SEQ shape, got {}", flimsy),
        },
        Star(ref body) | Plus(ref body) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("REP")) {
                    panic!("Need a REP, got {}", flimsy_parts[0])
                }

                let mut reps = vec![];
                for flimsy_part in flimsy_parts[1..].iter() {
                    reps.push(parse_flimsy_mbe(flimsy_part, &*body).unwrap());
                }
                Some(EnvMBE::new_from_anon_repeat(reps))
            }
            _ => panic!("Needed a REP shape, got {}", flimsy),
        },
        Alt(ref subs) => {
            // HACK: always pick the first branch of the `Alt`
            // (mainly affects unquotation, where it skips the type annotation)
            parse_flimsy_mbe(flimsy, &*subs[0])
        }
        Named(name, ref body) => Some(EnvMBE::new_from_leaves(
            crate::util::assoc::Assoc::new().set(*name, parse_flimsy_ast(flimsy, &*body)),
        )),
        SynImport(_, _, _) => panic!("`SynImport` can't work without a real parser"),
        NameImport(_, _) => panic!("`NameImport` should live underneath `Named`: {:?}", grammar),
        _ => unimplemented!("Can't handle {:?}", grammar),
    }
}

fn parse_flimsy_ast(flimsy: &Ast, grammar: &FormPat) -> Ast {
    use crate::grammar::FormPat::*;

    match grammar {
        Anyways(ref a) => a.clone(),
        Impossible => unimplemented!(),
        Scan(_) => flimsy.clone(),
        Literal(_, _) => Trivial,
        VarRef(_) => match flimsy {
            VariableReference(a) => VariableReference(*a),
            non_atom => panic!("Needed an atom, got {}", non_atom),
        },
        NameImport(body, beta) => {
            ExtendEnv(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        NameImportPhaseless(body, beta) => {
            ExtendEnvPhaseless(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        QuoteDeepen(body, pos) => QuoteMore(Box::new(parse_flimsy_ast(flimsy, &*body)), *pos),
        QuoteEscape(body, depth) => QuoteLess(Box::new(parse_flimsy_ast(flimsy, &*body)), *depth),

        Call(name) => {
            // HACK: don't descend into `Call(n("DefaultAtom"))
            if *name == n("DefaultAtom") || *name == n("AtomNotInPat") {
                match flimsy {
                    VariableReference(a) => Atom(*a),
                    non_atom => panic!("Needed an atom, got {}", non_atom),
                }
            } else {
                flimsy.clone()
            }
        }
        _ => unimplemented!("Can't handle {:?}", grammar),
    }
}
<|endoftext|>


<commit_before>use std::rc::Rc;

/// A SliceParser is a parser that parses some elements out of the beginning of
/// a slice and returns a parsed value along with the rest of the unparsed slice
pub trait SliceParser  {
  type I;
  type O;

  fn parse<'a>(&self, data: &'a[Self::I]) -> ParseResult<&'a [Self::I], Self::O>;
}

/// Combinator methods for slice parsers.  In most cases, these methods copy
/// the caller into a higher-order parser
pub trait ParserCombinator : SliceParser + Clone {

  /// Chain this parser with another parser, creating new parser that returns a
  /// tuple of their results
  fn then<P: SliceParser<I=Self::I>>(&self, p: P) -> ChainedParser<Self::I, Self,P> {
    ChainedParser{first: self.clone(), second: p}
  }

  /// Chain this parser with another parser, but toss the value from this parser
  fn then_r<P: ParserCombinator<I=Self::I>>(&self, p: P) -> MapParser<ChainedParser<Self::I, Self, P>, P::O> {
    self.then(p).map(|(_, t)| t)
  }

  /// Chain this parser with another parser, but toss the value from the other parser
  fn then_l<P: ParserCombinator<I=Self::I>>(&self, p: P) -> MapParser<ChainedParser<Self::I, Self, P>, Self::O> {
    self.then(p).map(|(t, _)| t)
  }

  /// Create a new parser that will repeat this parser until it returns an error
  fn repeat(&self) -> RepeatParser<Self> {
    RepeatParser{parser: self.clone()}
  }

  /// Map the value of this parser
  fn map<T, F: 'static + Fn(Self::O) -> T>(&self, f: F) -> MapParser<Self, T> {
    MapParser{parser: self.clone(), mapper: Rc::new(Box::new(f))}
  }

  /// Create a disjunction with another parser.  If this parser produces an error, the other parser will be used
  fn or<P: SliceParser<I=Self::I, O=Self::O>>(&self, p: P) -> OrParser<Self::I, Self::O, Self,P> {
    OrParser{first: self.clone(), second: p}
  }

}

pub type ParseResult<I,O> = Result<(O, I), String>;

/// Create a parser that only recognizes the given literal value
pub fn lit<T: Eq + Clone>(l: T) -> LiteralParser<T> {
  LiteralParser{literal: l}
}

/// Create a parser that will return Some if the given parser is successful, None otherwise
pub fn opt<T: SliceParser>(t: T) -> OptionParser<T> {
  OptionParser{parser: t}
}

/// Create a lazily evaluated parser from a function.  This can be used to generate recursive parsers
pub fn recursive<I,O, F:  Fn() -> Box<SliceParser<I=I,O=O>>>(f: F) -> RecursiveParser<I,O,F> {
  RecursiveParser{parser: Rc::new(f)}
}

pub fn matcher<T: Clone, U, F: 'static + Fn(T) -> Option<U>>(f: F) -> MatchParser<T, U> {
  MatchParser{matcher: Rc::new(Box::new(f))}
}

//////////////////////// STRUCTS /////////////////////////////////////////////

/// A Chained parser contains two parsers that will be used in sequence to
/// create a tuple of parsed values
pub struct ChainedParser<C, A: SliceParser<I=C>, B: SliceParser<I=C>> {
  first: A,
  second: B,
}
impl<C, A: SliceParser<I=C>, B: SliceParser<I=C>> SliceParser for ChainedParser<C, A, B> {
  type I = C;
  type O = (A::O,B::O);

  fn parse<'a>(&self, data: &'a[Self::I]) -> ParseResult<&'a [Self::I], Self::O>{
    match self.first.parse(data) {
      Ok((a, d2)) => match self.second.parse(d2) {
        Ok((b, remain)) => Ok(((a, b), remain)),
        Err(err) => Err(err)
      },
      Err(err) => Err(err)
    }
  }
}

impl<C, A: ParserCombinator<I=C>, B: ParserCombinator<I=C>>  Clone for ChainedParser<C, A, B> {

  fn clone(&self) -> Self {
    ChainedParser{first: self.first.clone(), second: self.second.clone()}
  }
}

impl<C, A: ParserCombinator<I=C>, B: ParserCombinator<I=C>>  ParserCombinator for ChainedParser<C, A, B> {}

/// A LiteralParser looks for an exact match of the given item at the beginning
// of the slice
#[derive(Clone)]
pub struct LiteralParser< T: Eq + Clone> {
  pub literal: T,
}

impl<T: Eq + Clone> SliceParser for LiteralParser< T> {
  type I = T;
  type O = T;

  fn parse<'a>(&self, data: &'a [T]) -> ParseResult<&'a [T], T> {
    if data.len() < 1 {
      return Err(format!("ran out of data"))
    }
    if data[0] == self.literal {
      Ok((data[0].clone(), &data[1..]))
    } else {
      Err(format!("Literal mismatch"))
    }
  }
}

impl<T: Eq + Clone> ParserCombinator for LiteralParser<T>{}

/// A Parser that repeats the given parser until it encounters an error.  A
/// vector of the accumulated parsed values is returned
pub struct RepeatParser<P: SliceParser> {
  parser: P
}
impl<T: SliceParser> SliceParser for RepeatParser<T> {
  type I = T::I;
  type O = Vec<T::O>;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    let mut remain = data;
    let mut v: Vec<T::O> = Vec::new();
    loop {
      match self.parser.parse(remain.clone()) {
        Ok((result, rest)) => {
          v.push(result);
          remain = rest;
        }
        Err(_) => {
          return Ok((v, remain));
        }
      }
    }
  }
}

impl<T: ParserCombinator> ParserCombinator for RepeatParser<T> {}

impl<T: ParserCombinator> Clone for RepeatParser<T> {
  fn clone(&self) -> Self {
    RepeatParser{parser: self.parser.clone()}
  }
}

/// A Parser that uses a closure to map the result of another parser
pub struct MapParser<P: SliceParser, T> {
  parser: P,
  mapper: Rc<Box<Fn(P::O) -> T>>,
}

impl<P: SliceParser, T> SliceParser for MapParser<P,T> {
  type I = P::I;
  type O = T;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    self.parser.parse(data).map(|(output, input)| ((self.mapper)(output), input))
  }

}

impl<P: ParserCombinator, T> Clone for MapParser<P,T> {

  fn clone(&self) -> Self {
    MapParser{parser: self.parser.clone(), mapper: self.mapper.clone()}
  }
}

impl<P: ParserCombinator, T> ParserCombinator for MapParser<P,T> {}

pub struct OrParser<I,O, S: SliceParser<I=I,O=O>, T: SliceParser<I=I,O=O>> {
  first: S,
  second: T,
}

impl<I,O, S: SliceParser<I=I,O=O>, T: SliceParser<I=I,O=O>> SliceParser for OrParser<I,O,S,T> {
  type I = I;
  type O = O;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    match self.first.parse(data.clone()) {
      Ok((a, d2)) => Ok((a, d2)),
      Err(_) => match self.second.parse(data.clone()) {
        Ok((b, remain)) => Ok((b, remain)),
        Err(err) => Err(err)
      }
    }
  }
}

impl<I,O, S: ParserCombinator<I=I,O=O>, T: ParserCombinator<I=I,O=O>> Clone for OrParser<I,O,S,T> {

  fn clone(&self) -> Self {
    OrParser{first: self.first.clone(), second: self.second.clone()}
  }
}

impl<I,O, S: ParserCombinator<I=I,O=O>, T: ParserCombinator<I=I,O=O>> ParserCombinator for OrParser<I,O,S,T> {}

#[derive(Clone)]
pub struct OptionParser<P: SliceParser> {
  parser: P
}
impl<P: SliceParser> SliceParser for OptionParser<P> {
  type I = P::I;
  type O = Option<P::O>;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    match self.parser.parse(data.clone()) {
      Ok((result, rest))  => Ok((Some(result), rest)),
      Err(_)              => Ok((None, data)),
    }
  }
}

pub struct RecursiveParser<I, O, F> where F: Fn() -> Box<SliceParser<I=I,O=O>>{
  parser: Rc<F>
}

impl<I, O, F> SliceParser for RecursiveParser<I, O, F> where F: Fn() -> Box<SliceParser<I=I,O=O>> {

  type I = I;
  type O = O;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    (self.parser)().parse(data)
  }

}

impl<I, O, F> ParserCombinator for RecursiveParser<I, O, F> where F: Fn() -> Box<SliceParser<I=I,O=O>> {}

impl<I, O, F> Clone for RecursiveParser<I, O, F> where F: Fn() -> Box<SliceParser<I=I,O=O>> {
  fn clone(&self) -> Self {
    RecursiveParser{parser: self.parser.clone()}
  }
}

pub struct MatchParser<T: Clone, U> {
  matcher: Rc<Box<Fn(T) -> Option<U>>>
}

impl<T: Clone, U> SliceParser for MatchParser<T,U> {
  type I = T;
  type O = U;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    if data.len() < 1 {
      return Err(format!("ran out of data"))
    }
    match (self.matcher)(data[0].clone()) {
      Some(u) => Ok((u, &data[1..])),
      None    => Err(format!("Match failed"))
    }
  }
}

impl<T: Clone, U> ParserCombinator for MatchParser<T,U> {}

impl<T: Clone, U> Clone for MatchParser<T,U> {

  fn clone(&self) -> Self {
    MatchParser{matcher: self.matcher.clone()}
  }

}

<commit_msg>removing unnecessary type params<commit_after>use std::rc::Rc;

/// A SliceParser is a parser that parses some elements out of the beginning of
/// a slice and returns a parsed value along with the rest of the unparsed slice
pub trait SliceParser  {
  type I;
  type O;

  fn parse<'a>(&self, data: &'a[Self::I]) -> ParseResult<&'a [Self::I], Self::O>;
}

/// Combinator methods for slice parsers.  In most cases, these methods copy
/// the caller into a higher-order parser
pub trait ParserCombinator : SliceParser + Clone {

  /// Chain this parser with another parser, creating new parser that returns a
  /// tuple of their results
  fn then<P: SliceParser<I=Self::I>>(&self, p: P) -> ChainedParser<Self,P> {
    ChainedParser{first: self.clone(), second: p}
  }

  /// Chain this parser with another parser, but toss the value from this parser
  fn then_r<P: ParserCombinator<I=Self::I>>(&self, p: P) -> MapParser<ChainedParser<Self, P>, P::O> {
    self.then(p).map(|(_, t)| t)
  }

  /// Chain this parser with another parser, but toss the value from the other parser
  fn then_l<P: ParserCombinator<I=Self::I>>(&self, p: P) -> MapParser<ChainedParser<Self, P>, Self::O> {
    self.then(p).map(|(t, _)| t)
  }

  /// Create a new parser that will repeat this parser until it returns an error
  fn repeat(&self) -> RepeatParser<Self> {
    RepeatParser{parser: self.clone()}
  }

  /// Map the value of this parser
  fn map<T, F: 'static + Fn(Self::O) -> T>(&self, f: F) -> MapParser<Self, T> {
    MapParser{parser: self.clone(), mapper: Rc::new(Box::new(f))}
  }

  /// Create a disjunction with another parser.  If this parser produces an error, the other parser will be used
  fn or<P: SliceParser<I=Self::I, O=Self::O>>(&self, p: P) -> OrParser<Self::I, Self::O, Self,P> {
    OrParser{first: self.clone(), second: p}
  }

}

pub type ParseResult<I,O> = Result<(O, I), String>;

/// Create a parser that only recognizes the given literal value
pub fn lit<T: Eq + Clone>(l: T) -> LiteralParser<T> {
  LiteralParser{literal: l}
}

/// Create a parser that will return Some if the given parser is successful, None otherwise
pub fn opt<T: SliceParser>(t: T) -> OptionParser<T> {
  OptionParser{parser: t}
}

/// Create a lazily evaluated parser from a function.  This can be used to generate recursive parsers
pub fn recursive<I,O, F:  Fn() -> Box<SliceParser<I=I,O=O>>>(f: F) -> RecursiveParser<I,O,F> {
  RecursiveParser{parser: Rc::new(f)}
}

pub fn matcher<T: Clone, U, F: 'static + Fn(T) -> Option<U>>(f: F) -> MatchParser<T, U> {
  MatchParser{matcher: Rc::new(Box::new(f))}
}

//////////////////////// STRUCTS /////////////////////////////////////////////

/// A Chained parser contains two parsers that will be used in sequence to
/// create a tuple of parsed values
pub struct ChainedParser<A,B> {
  first: A,
  second: B,
}
impl<C, A: SliceParser<I=C>, B: SliceParser<I=C>> SliceParser for ChainedParser<A, B> {
  type I = C;
  type O = (A::O,B::O);

  fn parse<'a>(&self, data: &'a[Self::I]) -> ParseResult<&'a [Self::I], Self::O>{
    match self.first.parse(data) {
      Ok((a, d2)) => match self.second.parse(d2) {
        Ok((b, remain)) => Ok(((a, b), remain)),
        Err(err) => Err(err)
      },
      Err(err) => Err(err)
    }
  }
}

impl<C, A: ParserCombinator<I=C>, B: ParserCombinator<I=C>>  Clone for ChainedParser<A, B> {

  fn clone(&self) -> Self {
    ChainedParser{first: self.first.clone(), second: self.second.clone()}
  }
}

impl<C, A: ParserCombinator<I=C>, B: ParserCombinator<I=C>>  ParserCombinator for ChainedParser<A, B> {}

/// A LiteralParser looks for an exact match of the given item at the beginning
// of the slice
#[derive(Clone)]
pub struct LiteralParser< T: Eq + Clone> {
  pub literal: T,
}

impl<T: Eq + Clone> SliceParser for LiteralParser< T> {
  type I = T;
  type O = T;

  fn parse<'a>(&self, data: &'a [T]) -> ParseResult<&'a [T], T> {
    if data.len() < 1 {
      return Err(format!("ran out of data"))
    }
    if data[0] == self.literal {
      Ok((data[0].clone(), &data[1..]))
    } else {
      Err(format!("Literal mismatch"))
    }
  }
}

impl<T: Eq + Clone> ParserCombinator for LiteralParser<T>{}

/// A Parser that repeats the given parser until it encounters an error.  A
/// vector of the accumulated parsed values is returned
pub struct RepeatParser<P: SliceParser> {
  parser: P
}
impl<T: SliceParser> SliceParser for RepeatParser<T> {
  type I = T::I;
  type O = Vec<T::O>;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    let mut remain = data;
    let mut v: Vec<T::O> = Vec::new();
    loop {
      match self.parser.parse(remain.clone()) {
        Ok((result, rest)) => {
          v.push(result);
          remain = rest;
        }
        Err(_) => {
          return Ok((v, remain));
        }
      }
    }
  }
}

impl<T: ParserCombinator> ParserCombinator for RepeatParser<T> {}

impl<T: ParserCombinator> Clone for RepeatParser<T> {
  fn clone(&self) -> Self {
    RepeatParser{parser: self.parser.clone()}
  }
}

/// A Parser that uses a closure to map the result of another parser
pub struct MapParser<P: SliceParser, T> {
  parser: P,
  mapper: Rc<Box<Fn(P::O) -> T>>,
}

impl<P: SliceParser, T> SliceParser for MapParser<P,T> {
  type I = P::I;
  type O = T;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    self.parser.parse(data).map(|(output, input)| ((self.mapper)(output), input))
  }

}

impl<P: ParserCombinator, T> Clone for MapParser<P,T> {

  fn clone(&self) -> Self {
    MapParser{parser: self.parser.clone(), mapper: self.mapper.clone()}
  }
}

impl<P: ParserCombinator, T> ParserCombinator for MapParser<P,T> {}

pub struct OrParser<I,O, S: SliceParser<I=I,O=O>, T: SliceParser<I=I,O=O>> {
  first: S,
  second: T,
}

impl<I,O, S: SliceParser<I=I,O=O>, T: SliceParser<I=I,O=O>> SliceParser for OrParser<I,O,S,T> {
  type I = I;
  type O = O;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    match self.first.parse(data.clone()) {
      Ok((a, d2)) => Ok((a, d2)),
      Err(_) => match self.second.parse(data.clone()) {
        Ok((b, remain)) => Ok((b, remain)),
        Err(err) => Err(err)
      }
    }
  }
}

impl<I,O, S: ParserCombinator<I=I,O=O>, T: ParserCombinator<I=I,O=O>> Clone for OrParser<I,O,S,T> {

  fn clone(&self) -> Self {
    OrParser{first: self.first.clone(), second: self.second.clone()}
  }
}

impl<I,O, S: ParserCombinator<I=I,O=O>, T: ParserCombinator<I=I,O=O>> ParserCombinator for OrParser<I,O,S,T> {}

#[derive(Clone)]
pub struct OptionParser<P: SliceParser> {
  parser: P
}
impl<P: SliceParser> SliceParser for OptionParser<P> {
  type I = P::I;
  type O = Option<P::O>;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    match self.parser.parse(data.clone()) {
      Ok((result, rest))  => Ok((Some(result), rest)),
      Err(_)              => Ok((None, data)),
    }
  }
}

pub struct RecursiveParser<I, O, F> where F: Fn() -> Box<SliceParser<I=I,O=O>>{
  parser: Rc<F>
}

impl<I, O, F> SliceParser for RecursiveParser<I, O, F> where F: Fn() -> Box<SliceParser<I=I,O=O>> {

  type I = I;
  type O = O;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    (self.parser)().parse(data)
  }

}

impl<I, O, F> ParserCombinator for RecursiveParser<I, O, F> where F: Fn() -> Box<SliceParser<I=I,O=O>> {}

impl<I, O, F> Clone for RecursiveParser<I, O, F> where F: Fn() -> Box<SliceParser<I=I,O=O>> {
  fn clone(&self) -> Self {
    RecursiveParser{parser: self.parser.clone()}
  }
}

pub struct MatchParser<T: Clone, U> {
  matcher: Rc<Box<Fn(T) -> Option<U>>>
}

impl<T: Clone, U> SliceParser for MatchParser<T,U> {
  type I = T;
  type O = U;

  fn parse<'a>(&self, data: &'a [Self::I]) -> ParseResult<&'a [Self::I], Self::O> {
    if data.len() < 1 {
      return Err(format!("ran out of data"))
    }
    match (self.matcher)(data[0].clone()) {
      Some(u) => Ok((u, &data[1..])),
      None    => Err(format!("Match failed"))
    }
  }
}

impl<T: Clone, U> ParserCombinator for MatchParser<T,U> {}

impl<T: Clone, U> Clone for MatchParser<T,U> {

  fn clone(&self) -> Self {
    MatchParser{matcher: self.matcher.clone()}
  }

}

<|endoftext|>


<commit_before>use Prefab;
use calx::{self, hex_neighbors, Dijkstra};
use euclid::{point2, vec2};
use form::{self, Form};
use image::{self, GenericImage, SubImage};
use location::{Location, Portal, Sector};
use mapgen::{self, MapGen, Size2D, Vault, VaultCell};
use rand::{seq, Rand, Rng, SeedableRng};
use serde;
use std::collections::{BTreeSet, HashMap};
use std::io::Cursor;
use std::iter::FromIterator;
use std::slice;
use terrain::Terrain;
use world::Loadout;

/// Static generated world.
pub struct Worldgen {
    seed: u32,
    terrain: HashMap<Location, Terrain>,
    portals: HashMap<Location, Portal>,
    spawns: Vec<(Location, Loadout)>,
    player_entry: Location,
}

impl Worldgen {
    pub fn new(seed: u32) -> Worldgen {
        let mut ret = Worldgen {
            seed: seed,
            terrain: HashMap::new(),
            portals: HashMap::new(),
            spawns: Vec::new(),
            player_entry: Location::new(25, 0, 0),
        };

        ret.load_map_bitmap(
            Location::new(0, 0, 0),
            include_bytes!("../assets/overland.png"),
        );

        let mut rng: ::Rng = SeedableRng::from_seed([seed, seed, seed, seed]);

        let mut cave_entrance = Location::new(40, 12, 0);
        ret.cave_entrance(cave_entrance);
        ret.terrain.insert(cave_entrance, Terrain::Gate);
        ret.terrain
            .insert(cave_entrance + vec2(1, 1), Terrain::Empty);

        for depth in 1..11 {
            let up_stairs;
            let down_stairs;
            let mut spawns = Vec::new();

            {
                let mut digger = SectorDigger::new(&mut ret, Sector::new(0, 0, depth as i16));
                let domain = digger.domain();
                mapgen::Caves.dig(&mut rng, &mut digger, domain);

                up_stairs = digger.up_portal.expect("Mapgen didn't create stairs up");
                down_stairs = digger
                    .down_portal
                    .expect("Mapgen didn't create stairs down");

                // Spawn
                digger.clear_spawns_near_entrance(12);

                let mut spawn_locs =
                    seq::sample_iter(&mut rng, digger.spawn_region.iter(), 20).unwrap();
                let n_spawns = spawn_locs.len();

                let items = Form::filter(|f| f.is_item() && f.at_depth(depth));
                for &loc in spawn_locs.drain(0..n_spawns / 2) {
                    spawns.push((
                        loc,
                        form::rand(&mut rng, &items)
                            .expect("No item spawn")
                            .loadout
                            .clone(),
                    ))
                }

                let mobs = Form::filter(|f| f.is_mob() && f.at_depth(depth));
                for &loc in spawn_locs {
                    spawns.push((
                        loc,
                        form::rand(&mut rng, &mobs)
                            .expect("No mob spawn")
                            .loadout
                            .clone(),
                    ))
                }
            }

            ret.spawns.extend(spawns.into_iter());
            ret.portal(cave_entrance, up_stairs + vec2(1, 1));
            ret.portal(up_stairs, cave_entrance - vec2(1, 1));
            cave_entrance = down_stairs;

            // TODO: Generator needs an option to not generate stairs down on bottom level
        }

        ret
    }

    fn load_prefab(&mut self, origin: Location, prefab: &Prefab) {
        for (&p, &(ref terrain, ref entities)) in prefab.iter() {
            let loc = origin + p;

            self.terrain.insert(loc, *terrain);

            for spawn in entities.iter() {
                if spawn == "player" {
                    self.player_entry = loc;
                } else {
                    let form = Form::named(spawn)
                        .expect(&format!("Bad prefab: Form '{}' not found!", spawn));
                    self.spawns.push((loc, form.loadout.clone()));
                }
            }
        }
    }

    fn load_map_bitmap(&mut self, origin: Location, data: &[u8]) {
        let mut image = image::load(Cursor::new(data), image::ImageFormat::PNG).unwrap();
        // Skip the bottom horizontal line, it's used to store metadata pixels.
        let (w, h) = (image.width(), image.height());
        let input_map = SubImage::new(&mut image, 0, 0, w, h - 1);

        let prefab = calx::Prefab::parse(input_map).expect("Invalid overworld image map");

        self.terrain.extend(
            prefab
                .into_iter()
                .filter_map(|(p, c)| Terrain::from_color(c).map(|t| (origin + p, t))),
        );
    }

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

    pub fn get_terrain(&self, loc: Location) -> Terrain {
        if let Some(&t) = self.terrain.get(&loc) {
            t
        } else {
            self.default_terrain(loc)
        }
    }

    fn default_terrain(&self, loc: Location) -> Terrain {
        use Terrain::*;
        if loc.z == 0 {
            /*
            match loc.noise() {
                n if n > 0.8 => Tree,
                n if n > -0.8 => Grass,
                _ => Water,
            }
            */
            Ground
        } else {
            Rock
        }
    }

    pub fn get_portal(&self, loc: Location) -> Option<Location> {
        self.portals.get(&loc).map(|&p| loc + p)
    }

    pub fn spawns(&self) -> slice::Iter<(Location, Loadout)> { self.spawns.iter() }

    pub fn player_entry(&self) -> Location { self.player_entry }

    /// Make a cave entrance going down.
    fn cave_entrance(&mut self, loc: Location) {
        const DOWNBOUND_ENCLOSURE: [(i32, i32); 5] = [(1, 0), (0, 1), (2, 1), (1, 2), (2, 2)];

        for &v in &DOWNBOUND_ENCLOSURE {
            let loc = loc + vec2(v.0, v.1);
            self.terrain.insert(loc, Terrain::Rock);
        }

        self.terrain.insert(loc, Terrain::Ground);
        self.terrain.insert(loc + vec2(1, 1), Terrain::Ground);
    }

    /// Punch a (one-way) portal between two points.
    fn portal(&mut self, origin: Location, destination: Location) {
        self.portals
            .insert(origin, Portal::new(origin, destination));
    }
}

impl serde::Serialize for Worldgen {
    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        self.seed.serialize(s)
    }
}

impl<'a> serde::Deserialize<'a> for Worldgen {
    fn deserialize<D: serde::Deserializer<'a>>(d: D) -> Result<Self, D::Error> {
        Ok(Worldgen::new(serde::Deserialize::deserialize(d)?))
    }
}

struct SectorDigger<'a> {
    worldgen: &'a mut Worldgen,
    sector: Sector,
    up_portal: Option<Location>,
    down_portal: Option<Location>,
    spawn_region: BTreeSet<Location>,
}

impl<'a> SectorDigger<'a> {
    fn new(worldgen: &'a mut Worldgen, sector: Sector) -> SectorDigger<'a> {
        SectorDigger {
            worldgen,
            sector,
            up_portal: None,
            down_portal: None,
            spawn_region: BTreeSet::new(),
        }
    }

    // TODO return impl
    fn domain(&self) -> Vec<mapgen::Point2D> {
        let mut ret = Vec::new();

        let sector_origin = self.sector.origin();
        let mapgen_origin = mapgen::Point2D::zero();

        for loc in self.sector.iter() {
            let pos = mapgen_origin
                + sector_origin
                    .v2_at(loc)
                    .expect("Sector points are not Euclidean");

            // Okay, this part is a bit hairy, hang on.
            // Sectors are arranged in a rectangular grid, so there are some cells where you can
            // step diagonally across two sector boundaries. However, we want to pretend that
            // sectors are only connected in the four cardinal directions, so we omit these cells
            // from the domain to prevent surprise holes between two diagonally adjacent shafts.
            if Self::is_next_to_diagonal_sector(loc) {
                continue;
            }

            ret.push(pos);
        }

        ret
    }

    fn loc(&self, pos: mapgen::Point2D) -> Location { self.sector.origin() + pos.to_vector() }

    fn set(&mut self, loc: Location, terrain: Terrain) {
        if terrain != Terrain::Gate {
            debug_assert_ne!(self.up_portal, Some(loc), "mapgen overwriting exit");
            debug_assert_ne!(self.down_portal, Some(loc), "mapgen overwriting exit");
        }

        // FIXME: Mapgen currently trips this with exit placement
        // debug_assert!(
        //     !Self::is_next_to_diagonal_sector(loc) &&
        //     self.sector.iter().find(|x| x == &loc).is_some(),
        //     "Setting a location outsides sector area");

        self.worldgen.terrain.insert(loc, terrain);
    }

    /// Set with some extra heuristics
    fn smart_set(&mut self, loc: Location, terrain: Terrain) {
        if let Some(&previous) = self.worldgen.terrain.get(&loc) {
            // Never turn terrain already set as passable as impassable.
            if !previous.blocks_walk() && terrain.blocks_walk() {
                return;
            }

            // Don't clobber doors, period.
            if previous == Terrain::Door {
                return;
            }
        }
        self.set(loc, terrain);
    }

    fn clear_spawns_near_entrance(&mut self, range: u32) {
        for loc in Dijkstra::new(
            vec![self.up_portal.expect("No entrance generated")],
            |loc| {
                self.worldgen
                    .terrain
                    .get(loc)
                    .cloned()
                    .unwrap_or(Terrain::Empty)
                    .is_open()
            },
            10_000,
        ).weights
            .iter()
            .filter_map(|(loc, &w)| if w <= range { Some(loc) } else { None })
        {
            self.spawn_region.remove(loc);
        }
    }

    fn is_next_to_diagonal_sector(loc: Location) -> bool {
        hex_neighbors(loc)
            .map(|x| x.sector().taxicab_distance(loc.sector()))
            .any(|d| d > 1)
    }
}

impl<'a> mapgen::Dungeon for SectorDigger<'a> {
    type Vault = Room;

    fn sample_vault<R: Rng>(&mut self, rng: &mut R) -> Self::Vault { rng.gen() }

    fn dig_chamber<I: IntoIterator<Item = mapgen::Point2D>>(&mut self, area: I) {
        for pos in area {
            let loc = self.loc(pos);
            self.spawn_region.insert(loc);
            self.set(loc, Terrain::Ground);
        }
    }

    fn dig_corridor<I: IntoIterator<Item = mapgen::Point2D>>(&mut self, path: I) {
        for pos in path {
            let loc = self.loc(pos);
            // Do not spawn things in corridors.
            self.spawn_region.remove(&loc);
            self.set(loc, Terrain::Ground);
        }
    }

    fn place_vault(&mut self, vault: &Self::Vault, pos: mapgen::Point2D) {
        use mapgen::VaultCell::*;
        let points: Vec<(mapgen::Point2D, VaultCell)> = vault.get_shape();
        for &(p, c) in &points {
            let loc = self.loc(pos + p.to_vector());

            match c {
                Interior => {
                    self.set(loc, Terrain::Ground);
                    self.spawn_region.insert(loc);
                }
                DiggableWall | UndiggableWall => self.smart_set(loc, Terrain::Wall),
            }
        }
    }

    fn add_door(&mut self, pos: mapgen::Point2D) {
        let loc = self.loc(pos);
        self.spawn_region.remove(&loc);
        self.set(loc, Terrain::Door);
    }

    fn add_up_stairs(&mut self, pos: mapgen::Point2D) {
        let loc = self.loc(pos);
        self.up_portal = Some(loc);
        self.spawn_region.remove(&loc);
        // TODO: Add debug assertion that up-stairs are dug in the upper wall of a room
        self.set(loc, Terrain::Gate);
    }

    fn add_down_stairs(&mut self, pos: mapgen::Point2D) {
        let loc = self.loc(pos);
        self.down_portal = Some(loc);
        self.spawn_region.remove(&loc);
        self.set(loc, Terrain::Gate);

        // Visual hack to make the down-stairs show up better, carve out the rock blob that would
        // be drawn partially in front of it on screen.
        self.set(loc + vec2(1, 1), Terrain::Empty);
        // TODO: Add a debug assertion here to check that the exit location has proper enclosure to
        // begin with, both it and the rock cell in front of it need to be surrounded by undug
        // front tiles.
        self.spawn_region.remove(&(loc + vec2(1, 1))); // Just in case
    }
}

pub struct Room {
    size: Size2D,
}

impl mapgen::Vault for Room {
    fn get_shape<T: FromIterator<(mapgen::Point2D, VaultCell)>>(&self) -> T {
        (-1..self.size.height + 1)
            .flat_map(move |y| {
                (-1..self.size.width + 1).map(move |x| {
                    let x_wall = x == -1 || x == self.size.width;
                    let y_wall = y == -1 || y == self.size.height;
                    let p = point2(x, y);

                    if x_wall && y_wall {
                        (p, VaultCell::UndiggableWall)
                    } else if x_wall || y_wall {
                        (p, VaultCell::DiggableWall)
                    } else {
                        (p, VaultCell::Interior)
                    }
                })
            })
            .collect()
    }
}

impl Rand for Room {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        Room {
            size: Size2D::new(rng.gen_range(2, 8), rng.gen_range(2, 8)),
        }
    }
}

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

    #[test]
    fn test_determinism() {
        use rand::{self, Rand};

        let mut rng = rand::thread_rng();

        let seed = u32::rand(&mut rng);
        let mut gen = Worldgen::new(seed);

        // Build the value repeatedly using the same seed and see that they are all equal.
        for _ in 1..4 {
            let second = Worldgen::new(seed);

            assert_eq!(gen.seed, second.seed);
            // These can make huge printouts so don't use assert_eq that would try to print them to
            // stdout
            assert!(gen.terrain == second.terrain);
            assert!(gen.portals == second.portals);
            assert_eq!(gen.player_entry, second.player_entry);

            gen = second;
        }
    }
}
<commit_msg>Debuggable rooms<commit_after>use Prefab;
use calx::{self, hex_neighbors, Dijkstra};
use euclid::{point2, vec2};
use form::{self, Form};
use image::{self, GenericImage, SubImage};
use location::{Location, Portal, Sector};
use mapgen::{self, MapGen, Size2D, Vault, VaultCell};
use rand::{seq, Rand, Rng, SeedableRng};
use serde;
use std::collections::{BTreeSet, HashMap};
use std::io::Cursor;
use std::iter::FromIterator;
use std::slice;
use terrain::Terrain;
use world::Loadout;

/// Static generated world.
pub struct Worldgen {
    seed: u32,
    terrain: HashMap<Location, Terrain>,
    portals: HashMap<Location, Portal>,
    spawns: Vec<(Location, Loadout)>,
    player_entry: Location,
}

impl Worldgen {
    pub fn new(seed: u32) -> Worldgen {
        let mut ret = Worldgen {
            seed: seed,
            terrain: HashMap::new(),
            portals: HashMap::new(),
            spawns: Vec::new(),
            player_entry: Location::new(25, 0, 0),
        };

        ret.load_map_bitmap(
            Location::new(0, 0, 0),
            include_bytes!("../assets/overland.png"),
        );

        let mut rng: ::Rng = SeedableRng::from_seed([seed, seed, seed, seed]);

        let mut cave_entrance = Location::new(40, 12, 0);
        ret.cave_entrance(cave_entrance);
        ret.terrain.insert(cave_entrance, Terrain::Gate);
        ret.terrain
            .insert(cave_entrance + vec2(1, 1), Terrain::Empty);

        for depth in 1..11 {
            let up_stairs;
            let down_stairs;
            let mut spawns = Vec::new();

            {
                let mut digger = SectorDigger::new(&mut ret, Sector::new(0, 0, depth as i16));
                let domain = digger.domain();
                mapgen::Caves.dig(&mut rng, &mut digger, domain);

                up_stairs = digger.up_portal.expect("Mapgen didn't create stairs up");
                down_stairs = digger
                    .down_portal
                    .expect("Mapgen didn't create stairs down");

                // Spawn
                digger.clear_spawns_near_entrance(12);

                let mut spawn_locs =
                    seq::sample_iter(&mut rng, digger.spawn_region.iter(), 20).unwrap();
                let n_spawns = spawn_locs.len();

                let items = Form::filter(|f| f.is_item() && f.at_depth(depth));
                for &loc in spawn_locs.drain(0..n_spawns / 2) {
                    spawns.push((
                        loc,
                        form::rand(&mut rng, &items)
                            .expect("No item spawn")
                            .loadout
                            .clone(),
                    ))
                }

                let mobs = Form::filter(|f| f.is_mob() && f.at_depth(depth));
                for &loc in spawn_locs {
                    spawns.push((
                        loc,
                        form::rand(&mut rng, &mobs)
                            .expect("No mob spawn")
                            .loadout
                            .clone(),
                    ))
                }
            }

            ret.spawns.extend(spawns.into_iter());
            ret.portal(cave_entrance, up_stairs + vec2(1, 1));
            ret.portal(up_stairs, cave_entrance - vec2(1, 1));
            cave_entrance = down_stairs;

            // TODO: Generator needs an option to not generate stairs down on bottom level
        }

        ret
    }

    fn load_prefab(&mut self, origin: Location, prefab: &Prefab) {
        for (&p, &(ref terrain, ref entities)) in prefab.iter() {
            let loc = origin + p;

            self.terrain.insert(loc, *terrain);

            for spawn in entities.iter() {
                if spawn == "player" {
                    self.player_entry = loc;
                } else {
                    let form = Form::named(spawn)
                        .expect(&format!("Bad prefab: Form '{}' not found!", spawn));
                    self.spawns.push((loc, form.loadout.clone()));
                }
            }
        }
    }

    fn load_map_bitmap(&mut self, origin: Location, data: &[u8]) {
        let mut image = image::load(Cursor::new(data), image::ImageFormat::PNG).unwrap();
        // Skip the bottom horizontal line, it's used to store metadata pixels.
        let (w, h) = (image.width(), image.height());
        let input_map = SubImage::new(&mut image, 0, 0, w, h - 1);

        let prefab = calx::Prefab::parse(input_map).expect("Invalid overworld image map");

        self.terrain.extend(
            prefab
                .into_iter()
                .filter_map(|(p, c)| Terrain::from_color(c).map(|t| (origin + p, t))),
        );
    }

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

    pub fn get_terrain(&self, loc: Location) -> Terrain {
        if let Some(&t) = self.terrain.get(&loc) {
            t
        } else {
            self.default_terrain(loc)
        }
    }

    fn default_terrain(&self, loc: Location) -> Terrain {
        use Terrain::*;
        if loc.z == 0 {
            /*
            match loc.noise() {
                n if n > 0.8 => Tree,
                n if n > -0.8 => Grass,
                _ => Water,
            }
            */
            Ground
        } else {
            Rock
        }
    }

    pub fn get_portal(&self, loc: Location) -> Option<Location> {
        self.portals.get(&loc).map(|&p| loc + p)
    }

    pub fn spawns(&self) -> slice::Iter<(Location, Loadout)> { self.spawns.iter() }

    pub fn player_entry(&self) -> Location { self.player_entry }

    /// Make a cave entrance going down.
    fn cave_entrance(&mut self, loc: Location) {
        const DOWNBOUND_ENCLOSURE: [(i32, i32); 5] = [(1, 0), (0, 1), (2, 1), (1, 2), (2, 2)];

        for &v in &DOWNBOUND_ENCLOSURE {
            let loc = loc + vec2(v.0, v.1);
            self.terrain.insert(loc, Terrain::Rock);
        }

        self.terrain.insert(loc, Terrain::Ground);
        self.terrain.insert(loc + vec2(1, 1), Terrain::Ground);
    }

    /// Punch a (one-way) portal between two points.
    fn portal(&mut self, origin: Location, destination: Location) {
        self.portals
            .insert(origin, Portal::new(origin, destination));
    }
}

impl serde::Serialize for Worldgen {
    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
        self.seed.serialize(s)
    }
}

impl<'a> serde::Deserialize<'a> for Worldgen {
    fn deserialize<D: serde::Deserializer<'a>>(d: D) -> Result<Self, D::Error> {
        Ok(Worldgen::new(serde::Deserialize::deserialize(d)?))
    }
}

struct SectorDigger<'a> {
    worldgen: &'a mut Worldgen,
    sector: Sector,
    up_portal: Option<Location>,
    down_portal: Option<Location>,
    spawn_region: BTreeSet<Location>,
}

impl<'a> SectorDigger<'a> {
    fn new(worldgen: &'a mut Worldgen, sector: Sector) -> SectorDigger<'a> {
        SectorDigger {
            worldgen,
            sector,
            up_portal: None,
            down_portal: None,
            spawn_region: BTreeSet::new(),
        }
    }

    // TODO return impl
    fn domain(&self) -> Vec<mapgen::Point2D> {
        let mut ret = Vec::new();

        let sector_origin = self.sector.origin();
        let mapgen_origin = mapgen::Point2D::zero();

        for loc in self.sector.iter() {
            let pos = mapgen_origin
                + sector_origin
                    .v2_at(loc)
                    .expect("Sector points are not Euclidean");

            // Okay, this part is a bit hairy, hang on.
            // Sectors are arranged in a rectangular grid, so there are some cells where you can
            // step diagonally across two sector boundaries. However, we want to pretend that
            // sectors are only connected in the four cardinal directions, so we omit these cells
            // from the domain to prevent surprise holes between two diagonally adjacent shafts.
            if Self::is_next_to_diagonal_sector(loc) {
                continue;
            }

            ret.push(pos);
        }

        ret
    }

    fn loc(&self, pos: mapgen::Point2D) -> Location { self.sector.origin() + pos.to_vector() }

    fn set(&mut self, loc: Location, terrain: Terrain) {
        if terrain != Terrain::Gate {
            debug_assert_ne!(self.up_portal, Some(loc), "mapgen overwriting exit");
            debug_assert_ne!(self.down_portal, Some(loc), "mapgen overwriting exit");
        }

        // FIXME: Mapgen currently trips this with exit placement
        // debug_assert!(
        //     !Self::is_next_to_diagonal_sector(loc) &&
        //     self.sector.iter().find(|x| x == &loc).is_some(),
        //     "Setting a location outsides sector area");

        self.worldgen.terrain.insert(loc, terrain);
    }

    /// Set with some extra heuristics
    fn smart_set(&mut self, loc: Location, terrain: Terrain) {
        if let Some(&previous) = self.worldgen.terrain.get(&loc) {
            // Never turn terrain already set as passable as impassable.
            if !previous.blocks_walk() && terrain.blocks_walk() {
                return;
            }

            // Don't clobber doors, period.
            if previous == Terrain::Door {
                return;
            }
        }
        self.set(loc, terrain);
    }

    fn clear_spawns_near_entrance(&mut self, range: u32) {
        for loc in Dijkstra::new(
            vec![self.up_portal.expect("No entrance generated")],
            |loc| {
                self.worldgen
                    .terrain
                    .get(loc)
                    .cloned()
                    .unwrap_or(Terrain::Empty)
                    .is_open()
            },
            10_000,
        ).weights
            .iter()
            .filter_map(|(loc, &w)| if w <= range { Some(loc) } else { None })
        {
            self.spawn_region.remove(loc);
        }
    }

    fn is_next_to_diagonal_sector(loc: Location) -> bool {
        hex_neighbors(loc)
            .map(|x| x.sector().taxicab_distance(loc.sector()))
            .any(|d| d > 1)
    }
}

impl<'a> mapgen::Dungeon for SectorDigger<'a> {
    type Vault = Room;

    fn sample_vault<R: Rng>(&mut self, rng: &mut R) -> Self::Vault { rng.gen() }

    fn dig_chamber<I: IntoIterator<Item = mapgen::Point2D>>(&mut self, area: I) {
        for pos in area {
            let loc = self.loc(pos);
            self.spawn_region.insert(loc);
            self.set(loc, Terrain::Ground);
        }
    }

    fn dig_corridor<I: IntoIterator<Item = mapgen::Point2D>>(&mut self, path: I) {
        for pos in path {
            let loc = self.loc(pos);
            // Do not spawn things in corridors.
            self.spawn_region.remove(&loc);
            self.set(loc, Terrain::Ground);
        }
    }

    fn place_vault(&mut self, vault: &Self::Vault, pos: mapgen::Point2D) {
        use mapgen::VaultCell::*;
        let points: Vec<(mapgen::Point2D, VaultCell)> = vault.get_shape();
        for &(p, c) in &points {
            let loc = self.loc(pos + p.to_vector());

            match c {
                Interior => {
                    self.set(loc, Terrain::Ground);
                    self.spawn_region.insert(loc);
                }
                DiggableWall | UndiggableWall => self.smart_set(loc, Terrain::Wall),
            }
        }
    }

    fn add_door(&mut self, pos: mapgen::Point2D) {
        let loc = self.loc(pos);
        self.spawn_region.remove(&loc);
        self.set(loc, Terrain::Door);
    }

    fn add_up_stairs(&mut self, pos: mapgen::Point2D) {
        let loc = self.loc(pos);
        self.up_portal = Some(loc);
        self.spawn_region.remove(&loc);
        // TODO: Add debug assertion that up-stairs are dug in the upper wall of a room
        self.set(loc, Terrain::Gate);
    }

    fn add_down_stairs(&mut self, pos: mapgen::Point2D) {
        let loc = self.loc(pos);
        self.down_portal = Some(loc);
        self.spawn_region.remove(&loc);
        self.set(loc, Terrain::Gate);

        // Visual hack to make the down-stairs show up better, carve out the rock blob that would
        // be drawn partially in front of it on screen.
        self.set(loc + vec2(1, 1), Terrain::Empty);
        // TODO: Add a debug assertion here to check that the exit location has proper enclosure to
        // begin with, both it and the rock cell in front of it need to be surrounded by undug
        // front tiles.
        self.spawn_region.remove(&(loc + vec2(1, 1))); // Just in case
    }
}

#[derive(Debug)]
pub struct Room {
    size: Size2D,
}

impl mapgen::Vault for Room {
    fn get_shape<T: FromIterator<(mapgen::Point2D, VaultCell)>>(&self) -> T {
        (-1..self.size.height + 1)
            .flat_map(move |y| {
                (-1..self.size.width + 1).map(move |x| {
                    let x_wall = x == -1 || x == self.size.width;
                    let y_wall = y == -1 || y == self.size.height;
                    let p = point2(x, y);

                    if x_wall && y_wall {
                        (p, VaultCell::UndiggableWall)
                    } else if x_wall || y_wall {
                        (p, VaultCell::DiggableWall)
                    } else {
                        (p, VaultCell::Interior)
                    }
                })
            })
            .collect()
    }
}

impl Rand for Room {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        Room {
            size: Size2D::new(rng.gen_range(2, 8), rng.gen_range(2, 8)),
        }
    }
}

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

    #[test]
    fn test_determinism() {
        use rand::{self, Rand};

        let mut rng = rand::thread_rng();

        let seed = u32::rand(&mut rng);
        let mut gen = Worldgen::new(seed);

        // Build the value repeatedly using the same seed and see that they are all equal.
        for _ in 1..4 {
            let second = Worldgen::new(seed);

            assert_eq!(gen.seed, second.seed);
            // These can make huge printouts so don't use assert_eq that would try to print them to
            // stdout
            assert!(gen.terrain == second.terrain);
            assert!(gen.portals == second.portals);
            assert_eq!(gen.player_entry, second.player_entry);

            gen = second;
        }
    }
}
<|endoftext|>


<commit_before>use super::{html, Options};
use crate::binaries;
use chromiumoxide::{cdp::browser_protocol::page::CaptureScreenshotFormat, Browser, BrowserConfig};
use eyre::Result;
use futures::StreamExt;
use std::fs;
use stencila_schema::Node;

/// Encode a `Node` to a PNG file or Base64 encoded data URI.
pub async fn encode(node: &Node, output: &str, options: Option<Options>) -> Result<String> {
    let pngs = encode_to_pngs(&[node], options).await?;
    encode_to_output(&pngs[0], output)
}

/// Encode a list of `Node`s to PNGs (as bytes)
pub async fn encode_to_pngs(nodes: &[&Node], options: Option<Options>) -> Result<Vec<Vec<u8>>> {
    // Generate HTML for each node
    let mut html = String::new();
    for (index, node) in nodes.iter().enumerate() {
        let node_html = html::encode(
            node,
            Some(Options {
                standalone: false,
                bundle: true,
                ..Default::default()
            }),
        )?;
        html.push_str(&format!(r#"
{}
"#, index, node_html));
    }

    // Wrap the HTML with a header etc so that the theme is set and CSS is loaded
    let Options { theme, .. } = options.unwrap_or_default();
    let html = html::wrap_standalone(&html, &theme);

    // Launch the browser
    let chrome = binaries::require("chrome", "*").await?;
    let config = BrowserConfig::builder()
        .chrome_executable(chrome.path)
        .build()
        .expect("Should build config");
    let (browser, mut handler) = Browser::launch(config).await?;
    tokio::task::spawn(async move {
        loop {
            let _ = handler.next().await.unwrap();
        }
    });

    // Create a page and set its HTML
    let page = browser.new_page("about:blank").await?;
    page.set_content(html).await?.wait_for_navigation().await?;

    // Take a screenshot of each element
    let mut pngs = Vec::with_capacity(nodes.len());
    for index in 0..nodes.len() {
        let element = page.find_element(&format!("#node-{}", index)).await?;
        let bytes = element.screenshot(CaptureScreenshotFormat::Png).await?;
        pngs.push(bytes)
    }

    Ok(pngs)
}

/// Encode a PNG (as bytes) to a data URI or a file
pub fn encode_to_output(bytes: &[u8], output: &str) -> Result<String> {
    let content = if output.starts_with("data:") {
        ["data:image/png;base64,", &base64::encode(&bytes)].concat()
    } else {
        let path = if let Some(path) = output.strip_prefix("file://") {
            path
        } else {
            output
        };
        fs::write(path, bytes)?;
        ["file://", path].concat()
    };
    Ok(content)
}

#[cfg(test)]
mod tests {
    use super::*;
    use path_slash::PathExt;
    use stencila_schema::CodeChunk;

    #[tokio::test]
    async fn test_encode() -> Result<()> {
        let node = Node::CodeChunk(CodeChunk {
            programming_language: "python".to_string(),
            text: "print(\"Hello world!\")".to_string(),
            outputs: Some(vec![Node::String("Hello world!".to_string())]),
            ..Default::default()
        });

        let dir = tempfile::tempdir()?;
        let path = dir.path().join("temp.png");
        let output = encode(
            &node,
            &path.to_slash_lossy(),
            Some(Options {
                theme: "rpng".to_string(),
                ..Default::default()
            }),
        )
        .await?;
        assert_eq!(output, ["file://", &path.to_slash_lossy()].concat());
        assert!(path.exists());

        let data = encode(&node, "data://", None).await?;
        assert!(data.starts_with("data:image/png;base64,"));

        Ok(())
    }
}
<commit_msg>fix(PNG encoding): Return early if possible<commit_after>use super::{html, Options};
use crate::binaries;
use chromiumoxide::{cdp::browser_protocol::page::CaptureScreenshotFormat, Browser, BrowserConfig};
use eyre::Result;
use futures::StreamExt;
use std::fs;
use stencila_schema::Node;

/// Encode a `Node` to a PNG file or Base64 encoded data URI.
pub async fn encode(node: &Node, output: &str, options: Option<Options>) -> Result<String> {
    let pngs = encode_to_pngs(&[node], options).await?;
    encode_to_output(&pngs[0], output)
}

/// Encode a list of `Node`s to PNGs (as bytes)
pub async fn encode_to_pngs(nodes: &[&Node], options: Option<Options>) -> Result<Vec<Vec<u8>>> {
    // Return early if possible to avoid the following, including requiring Chrome.
    if nodes.is_empty() {
        return Ok(Vec::new())
    }

    // Generate HTML for each node
    let mut html = String::new();
    for (index, node) in nodes.iter().enumerate() {
        let node_html = html::encode(
            node,
            Some(Options {
                standalone: false,
                bundle: true,
                ..Default::default()
            }),
        )?;
        html.push_str(&format!(r#"
{}
"#, index, node_html));
    }

    // Wrap the HTML with a header etc so that the theme is set and CSS is loaded
    let Options { theme, .. } = options.unwrap_or_default();
    let html = html::wrap_standalone(&html, &theme);

    // Launch the browser
    let chrome = binaries::require("chrome", "*").await?;
    let config = BrowserConfig::builder()
        .chrome_executable(chrome.path)
        .build()
        .expect("Should build config");
    let (browser, mut handler) = Browser::launch(config).await?;
    tokio::task::spawn(async move {
        loop {
            let _ = handler.next().await.unwrap();
        }
    });

    // Create a page and set its HTML
    let page = browser.new_page("about:blank").await?;
    page.set_content(html).await?.wait_for_navigation().await?;

    // Take a screenshot of each element
    let mut pngs = Vec::with_capacity(nodes.len());
    for index in 0..nodes.len() {
        let element = page.find_element(&format!("#node-{}", index)).await?;
        let bytes = element.screenshot(CaptureScreenshotFormat::Png).await?;
        pngs.push(bytes)
    }

    Ok(pngs)
}

/// Encode a PNG (as bytes) to a data URI or a file
pub fn encode_to_output(bytes: &[u8], output: &str) -> Result<String> {
    let content = if output.starts_with("data:") {
        ["data:image/png;base64,", &base64::encode(&bytes)].concat()
    } else {
        let path = if let Some(path) = output.strip_prefix("file://") {
            path
        } else {
            output
        };
        fs::write(path, bytes)?;
        ["file://", path].concat()
    };
    Ok(content)
}

#[cfg(test)]
mod tests {
    use super::*;
    use path_slash::PathExt;
    use stencila_schema::CodeChunk;

    #[tokio::test]
    async fn test_encode() -> Result<()> {
        let node = Node::CodeChunk(CodeChunk {
            programming_language: "python".to_string(),
            text: "print(\"Hello world!\")".to_string(),
            outputs: Some(vec![Node::String("Hello world!".to_string())]),
            ..Default::default()
        });

        let dir = tempfile::tempdir()?;
        let path = dir.path().join("temp.png");
        let output = encode(
            &node,
            &path.to_slash_lossy(),
            Some(Options {
                theme: "rpng".to_string(),
                ..Default::default()
            }),
        )
        .await?;
        assert_eq!(output, ["file://", &path.to_slash_lossy()].concat());
        assert!(path.exists());

        let data = encode(&node, "data://", None).await?;
        assert!(data.starts_with("data:image/png;base64,"));

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;

use libimagstore::store::Store;
use libimagstore::storeid::IntoStoreId;
use libimagerror::trace::trace_error;

use chrono::offset::local::Local;
use chrono::Datelike;
use itertools::Itertools;
use chrono::naive::datetime::NaiveDateTime;

use entry::Entry;
use diaryid::DiaryId;
use error::DiaryError as DE;
use error::DiaryErrorKind as DEK;
use result::Result;
use iter::DiaryEntryIterator;
use is_in_diary::IsInDiary;

#[derive(Debug)]
pub struct Diary<'a> {
    store: &'a Store,
    name: &'a str,
}

impl<'a> Diary<'a> {

    pub fn open(store: &'a Store, name: &'a str) -> Diary<'a> {
        Diary {
            store: store,
            name: name,
        }
    }

    // create or get a new entry for today
    pub fn new_entry_today(&self) -> Result<Entry> {
        let dt  = Local::now();
        let ndt = dt.naive_local();
        let id  = DiaryId::new(String::from(self.name), ndt.year(), ndt.month(), ndt.day(), 0, 0);
        self.new_entry_by_id(id)
    }

    pub fn new_entry_by_id(&self, id: DiaryId) -> Result<Entry> {
        self.retrieve(id.with_diary_name(String::from(self.name)))
    }

    pub fn retrieve(&self, id: DiaryId) -> Result<Entry> {
        self.store
            .retrieve(id.into_storeid())
            .map(|fle| Entry::new(fle))
            .map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
    }

    // Get an iterator for iterating over all entries
    pub fn entries(&self) -> Result<DiaryEntryIterator<'a>> {
        self.store
            .retrieve_for_module("diary")
            .map(|iter| DiaryEntryIterator::new(self.name, self.store, iter))
            .map_err(|e| DE::new(DEK::StoreReadError, Some(Box::new(e))))
    }

    pub fn delete_entry(&self, entry: Entry) -> Result<()> {
        if !entry.is_in_diary(self.name) {
            return Err(DE::new(DEK::EntryNotInDiary, None));
        }
        let id = entry.get_location().clone();
        drop(entry);

        self.store.delete(id)
            .map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
    }

    pub fn get_youngest_entry(&self) -> Option<Result<Entry>> {
        match self.entries() {
            Err(e) => Some(Err(e)),
            Ok(entries) => {
                entries.sorted_by(|a, b| {
                    match (a, b) {
                        (&Ok(ref a), &Ok(ref b)) => {
                            let a : NaiveDateTime = a.diary_id().into();
                            let b : NaiveDateTime = b.diary_id().into();

                            a.cmp(&b)
                        },

                        (&Ok(_), &Err(ref e))  => {
                            trace_error(e);
                            Ordering::Less
                        },
                        (&Err(ref e), &Ok(_))  => {
                            trace_error(e);
                            Ordering::Greater
                        },
                        (&Err(ref e1), &Err(ref e2)) => {
                            trace_error(e1);
                            trace_error(e2);
                            Ordering::Equal
                        },
                    }
                }).into_iter().next()
            }
        }
    }

}

<commit_msg>Add Diary::name() getter<commit_after>use std::cmp::Ordering;

use libimagstore::store::Store;
use libimagstore::storeid::IntoStoreId;
use libimagerror::trace::trace_error;

use chrono::offset::local::Local;
use chrono::Datelike;
use itertools::Itertools;
use chrono::naive::datetime::NaiveDateTime;

use entry::Entry;
use diaryid::DiaryId;
use error::DiaryError as DE;
use error::DiaryErrorKind as DEK;
use result::Result;
use iter::DiaryEntryIterator;
use is_in_diary::IsInDiary;

#[derive(Debug)]
pub struct Diary<'a> {
    store: &'a Store,
    name: &'a str,
}

impl<'a> Diary<'a> {

    pub fn open(store: &'a Store, name: &'a str) -> Diary<'a> {
        Diary {
            store: store,
            name: name,
        }
    }

    // create or get a new entry for today
    pub fn new_entry_today(&self) -> Result<Entry> {
        let dt  = Local::now();
        let ndt = dt.naive_local();
        let id  = DiaryId::new(String::from(self.name), ndt.year(), ndt.month(), ndt.day(), 0, 0);
        self.new_entry_by_id(id)
    }

    pub fn new_entry_by_id(&self, id: DiaryId) -> Result<Entry> {
        self.retrieve(id.with_diary_name(String::from(self.name)))
    }

    pub fn retrieve(&self, id: DiaryId) -> Result<Entry> {
        self.store
            .retrieve(id.into_storeid())
            .map(|fle| Entry::new(fle))
            .map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
    }

    // Get an iterator for iterating over all entries
    pub fn entries(&self) -> Result<DiaryEntryIterator<'a>> {
        self.store
            .retrieve_for_module("diary")
            .map(|iter| DiaryEntryIterator::new(self.name, self.store, iter))
            .map_err(|e| DE::new(DEK::StoreReadError, Some(Box::new(e))))
    }

    pub fn delete_entry(&self, entry: Entry) -> Result<()> {
        if !entry.is_in_diary(self.name) {
            return Err(DE::new(DEK::EntryNotInDiary, None));
        }
        let id = entry.get_location().clone();
        drop(entry);

        self.store.delete(id)
            .map_err(|e| DE::new(DEK::StoreWriteError, Some(Box::new(e))))
    }

    pub fn get_youngest_entry(&self) -> Option<Result<Entry>> {
        match self.entries() {
            Err(e) => Some(Err(e)),
            Ok(entries) => {
                entries.sorted_by(|a, b| {
                    match (a, b) {
                        (&Ok(ref a), &Ok(ref b)) => {
                            let a : NaiveDateTime = a.diary_id().into();
                            let b : NaiveDateTime = b.diary_id().into();

                            a.cmp(&b)
                        },

                        (&Ok(_), &Err(ref e))  => {
                            trace_error(e);
                            Ordering::Less
                        },
                        (&Err(ref e), &Ok(_))  => {
                            trace_error(e);
                            Ordering::Greater
                        },
                        (&Err(ref e1), &Err(ref e2)) => {
                            trace_error(e1);
                            trace_error(e2);
                            Ordering::Equal
                        },
                    }
                }).into_iter().next()
            }
        }
    }

    pub fn name(&self) -> &'a str {
        &self.name
    }
}

<|endoftext|>


<commit_before>// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use super::{INTERRUPT_STATUS_CONFIG_CHANGED, INTERRUPT_STATUS_USED_RING};
use crate::pci::MsixConfig;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use sync::Mutex;
use sys_util::EventFd;

pub struct Interrupt {
    interrupt_status: Arc<AtomicUsize>,
    interrupt_evt: EventFd,
    interrupt_resample_evt: EventFd,
    msix_config: Option<Arc<Mutex<MsixConfig>>>,
}

impl Interrupt {
    pub fn new(
        interrupt_status: Arc<AtomicUsize>,
        interrupt_evt: EventFd,
        interrupt_resample_evt: EventFd,
        msix_config: Option<Arc<Mutex<MsixConfig>>>,
    ) -> Interrupt {
        Interrupt {
            interrupt_status,
            interrupt_evt,
            interrupt_resample_evt,
            msix_config,
        }
    }

    /// Virtqueue Interrupts From The Device
    ///
    /// If MSI-X is enabled in this device, MSI-X interrupt is preferred.
    /// Write to the irqfd to VMM to deliver virtual interrupt to the guest
    pub fn signal_used_queue(&self, vector: u16) {
        // Don't need to set ISR for MSI-X interrupts
        if let Some(msix_config) = &self.msix_config {
            let mut msix_config = msix_config.lock();
            if msix_config.enabled() {
                msix_config.trigger(vector);
                return;
            }
        }

        // Set BIT0 in ISR and inject the interrupt if it was not already pending.
        // Don't need to inject the interrupt if the guest hasn't processed it.
        if self
            .interrupt_status
            .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst)
            == 0
        {
            // Write to irqfd to inject INTx interrupt
            self.interrupt_evt.write(1).unwrap();
        }
    }

    /// Notification of Device Configuration Changes
    /// Set BIT1 in ISR and write to irqfd
    pub fn signal_config_changed(&self) {
        self.interrupt_status
            .fetch_or(INTERRUPT_STATUS_CONFIG_CHANGED as usize, Ordering::SeqCst);
        self.interrupt_evt.write(1).unwrap();
    }

    /// Handle interrupt resampling event
    pub fn interrupt_resample(&self) {
        let _ = self.interrupt_resample_evt.read();
        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
            self.interrupt_evt.write(1).unwrap();
        }
    }

    /// Return the reference of interrupt_resample_evt
    /// To keep the interface clean, this member is private.
    pub fn get_resample_evt(&self) -> &EventFd {
        &self.interrupt_resample_evt
    }
}
<commit_msg>devices: virtio: do not trigger MSI-X with no vector<commit_after>// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use super::{INTERRUPT_STATUS_CONFIG_CHANGED, INTERRUPT_STATUS_USED_RING, VIRTIO_MSI_NO_VECTOR};
use crate::pci::MsixConfig;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use sync::Mutex;
use sys_util::EventFd;

pub struct Interrupt {
    interrupt_status: Arc<AtomicUsize>,
    interrupt_evt: EventFd,
    interrupt_resample_evt: EventFd,
    msix_config: Option<Arc<Mutex<MsixConfig>>>,
}

impl Interrupt {
    pub fn new(
        interrupt_status: Arc<AtomicUsize>,
        interrupt_evt: EventFd,
        interrupt_resample_evt: EventFd,
        msix_config: Option<Arc<Mutex<MsixConfig>>>,
    ) -> Interrupt {
        Interrupt {
            interrupt_status,
            interrupt_evt,
            interrupt_resample_evt,
            msix_config,
        }
    }

    /// Virtqueue Interrupts From The Device
    ///
    /// If MSI-X is enabled in this device, MSI-X interrupt is preferred.
    /// Write to the irqfd to VMM to deliver virtual interrupt to the guest
    pub fn signal_used_queue(&self, vector: u16) {
        // Don't need to set ISR for MSI-X interrupts
        if let Some(msix_config) = &self.msix_config {
            let mut msix_config = msix_config.lock();
            if msix_config.enabled() {
                if vector != VIRTIO_MSI_NO_VECTOR {
                    msix_config.trigger(vector);
                }
                return;
            }
        }

        // Set BIT0 in ISR and inject the interrupt if it was not already pending.
        // Don't need to inject the interrupt if the guest hasn't processed it.
        if self
            .interrupt_status
            .fetch_or(INTERRUPT_STATUS_USED_RING as usize, Ordering::SeqCst)
            == 0
        {
            // Write to irqfd to inject INTx interrupt
            self.interrupt_evt.write(1).unwrap();
        }
    }

    /// Notification of Device Configuration Changes
    /// Set BIT1 in ISR and write to irqfd
    pub fn signal_config_changed(&self) {
        self.interrupt_status
            .fetch_or(INTERRUPT_STATUS_CONFIG_CHANGED as usize, Ordering::SeqCst);
        self.interrupt_evt.write(1).unwrap();
    }

    /// Handle interrupt resampling event
    pub fn interrupt_resample(&self) {
        let _ = self.interrupt_resample_evt.read();
        if self.interrupt_status.load(Ordering::SeqCst) != 0 {
            self.interrupt_evt.write(1).unwrap();
        }
    }

    /// Return the reference of interrupt_resample_evt
    /// To keep the interface clean, this member is private.
    pub fn get_resample_evt(&self) -> &EventFd {
        &self.interrupt_resample_evt
    }
}
<|endoftext|>


<commit_before>use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType};

const SUPPORTED_TYPES: &[SemanticTokenType] = &[
    SemanticTokenType::COMMENT,
    SemanticTokenType::KEYWORD,
    SemanticTokenType::STRING,
    SemanticTokenType::NUMBER,
    SemanticTokenType::REGEXP,
    SemanticTokenType::OPERATOR,
    SemanticTokenType::NAMESPACE,
    SemanticTokenType::TYPE,
    SemanticTokenType::STRUCT,
    SemanticTokenType::CLASS,
    SemanticTokenType::INTERFACE,
    SemanticTokenType::ENUM,
    SemanticTokenType::TYPE_PARAMETER,
    SemanticTokenType::FUNCTION,
    SemanticTokenType::MEMBER,
    SemanticTokenType::PROPERTY,
    SemanticTokenType::MACRO,
    SemanticTokenType::VARIABLE,
    SemanticTokenType::PARAMETER,
    SemanticTokenType::LABEL,
];

const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
    SemanticTokenModifier::DOCUMENTATION,
    SemanticTokenModifier::DECLARATION,
    SemanticTokenModifier::DEFINITION,
    SemanticTokenModifier::REFERENCE,
    SemanticTokenModifier::STATIC,
    SemanticTokenModifier::ABSTRACT,
    SemanticTokenModifier::DEPRECATED,
    SemanticTokenModifier::ASYNC,
    SemanticTokenModifier::VOLATILE,
    SemanticTokenModifier::READONLY,
];

pub(crate) fn supported_token_types() -> &'static [SemanticTokenType] {
    SUPPORTED_TYPES
}

pub(crate) fn supported_token_modifiers() -> &'static [SemanticTokenModifier] {
    SUPPORTED_MODIFIERS
}

#[derive(Default)]
pub(crate) struct SemanticTokensBuilder {
    prev_line: u32,
    prev_char: u32,
    data: Vec<SemanticToken>,
}

impl SemanticTokensBuilder {
    pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) {
        let mut push_line = range.start.line as u32;
        let mut push_char = range.start.character as u32;

        if !self.data.is_empty() {
            push_line -= self.prev_line;
            if push_line == 0 {
                push_char -= self.prev_char;
            }
        }

        // A token cannot be multiline
        let token_len = range.end.character - range.start.character;

        let token = SemanticToken {
            delta_line: push_line,
            delta_start: push_char,
            length: token_len as u32,
            token_type: token_index,
            token_modifiers_bitset: modifier_bitset,
        };

        self.data.push(token);

        self.prev_line = range.start.line as u32;
        self.prev_char = range.start.character as u32;
    }

    pub fn build(self) -> Vec<SemanticToken> {
        self.data
    }
}
<commit_msg>Add docs<commit_after>//! Semantic Tokens helpers

use lsp_types::{Range, SemanticToken, SemanticTokenModifier, SemanticTokenType};

const SUPPORTED_TYPES: &[SemanticTokenType] = &[
    SemanticTokenType::COMMENT,
    SemanticTokenType::KEYWORD,
    SemanticTokenType::STRING,
    SemanticTokenType::NUMBER,
    SemanticTokenType::REGEXP,
    SemanticTokenType::OPERATOR,
    SemanticTokenType::NAMESPACE,
    SemanticTokenType::TYPE,
    SemanticTokenType::STRUCT,
    SemanticTokenType::CLASS,
    SemanticTokenType::INTERFACE,
    SemanticTokenType::ENUM,
    SemanticTokenType::TYPE_PARAMETER,
    SemanticTokenType::FUNCTION,
    SemanticTokenType::MEMBER,
    SemanticTokenType::PROPERTY,
    SemanticTokenType::MACRO,
    SemanticTokenType::VARIABLE,
    SemanticTokenType::PARAMETER,
    SemanticTokenType::LABEL,
];

const SUPPORTED_MODIFIERS: &[SemanticTokenModifier] = &[
    SemanticTokenModifier::DOCUMENTATION,
    SemanticTokenModifier::DECLARATION,
    SemanticTokenModifier::DEFINITION,
    SemanticTokenModifier::REFERENCE,
    SemanticTokenModifier::STATIC,
    SemanticTokenModifier::ABSTRACT,
    SemanticTokenModifier::DEPRECATED,
    SemanticTokenModifier::ASYNC,
    SemanticTokenModifier::VOLATILE,
    SemanticTokenModifier::READONLY,
];

/// Token types that the server supports
pub(crate) fn supported_token_types() -> &'static [SemanticTokenType] {
    SUPPORTED_TYPES
}

/// Token modifiers that the server supports
pub(crate) fn supported_token_modifiers() -> &'static [SemanticTokenModifier] {
    SUPPORTED_MODIFIERS
}

/// Tokens are encoded relative to each other.
///
/// This is a direct port of https://github.com/microsoft/vscode-languageserver-node/blob/f425af9de46a0187adb78ec8a46b9b2ce80c5412/server/src/sematicTokens.proposed.ts#L45
#[derive(Default)]
pub(crate) struct SemanticTokensBuilder {
    prev_line: u32,
    prev_char: u32,
    data: Vec<SemanticToken>,
}

impl SemanticTokensBuilder {
    /// Push a new token onto the builder
    pub fn push(&mut self, range: Range, token_index: u32, modifier_bitset: u32) {
        let mut push_line = range.start.line as u32;
        let mut push_char = range.start.character as u32;

        if !self.data.is_empty() {
            push_line -= self.prev_line;
            if push_line == 0 {
                push_char -= self.prev_char;
            }
        }

        // A token cannot be multiline
        let token_len = range.end.character - range.start.character;

        let token = SemanticToken {
            delta_line: push_line,
            delta_start: push_char,
            length: token_len as u32,
            token_type: token_index,
            token_modifiers_bitset: modifier_bitset,
        };

        self.data.push(token);

        self.prev_line = range.start.line as u32;
        self.prev_char = range.start.character as u32;
    }

    pub fn build(self) -> Vec<SemanticToken> {
        self.data
    }
}
<|endoftext|>


<commit_before>//
// Usage:
//
//     b64-stream encode <infile> [outfile]
//     b64-stream decode <infile> [outfile]
//

extern mod encoding;

use io::Reader;
use io::ReaderUtil;
use path::Path;
use encoding::BASE64;
use encoding::Base64Writer;
use encoding::Base64Reader;

fn main() {
    let args = os::args();
    let argc = args.len();

    if argc < 3 {
        return;
    }

    let writer = if argc > 3 {
        io::mk_file_writer(&Path(args[3]), ~[io::Create, io::Truncate]).get()
    } else {
        io::stdout()
    };

    match args[1] {
        ~"encode" => encode(&Path(args[2]), writer),
        ~"decode" => decode(&Path(args[2]), writer),
        _         => return
    }
}

fn encode(filename: &Path, writer: io::Writer) {
    let writer = &Base64Writer(BASE64, writer);
    let reader = io::file_reader(filename).get();

    while !reader.eof() {
        let buf = reader.read_bytes(1024);
        writer.write(buf);
    }

    // FIXME Remove this line once we get drop intrinsics.
    writer.close();
}

fn decode(filename: &Path, writer: io::Writer) {
    let reader = io::file_reader(filename).get();
    let reader = &Base64Reader(BASE64, reader);

    while !reader.eof() {
        let buf = reader.read_bytes(1024);
        writer.write(buf);
    }
}
<commit_msg>examples: Use fixed-size vector on reading<commit_after>//
// Usage:
//
//     b64-stream encode <infile> [outfile]
//     b64-stream decode <infile> [outfile]
//

extern mod encoding;

use io::Reader;
use io::ReaderUtil;
use path::Path;
use encoding::BASE64;
use encoding::Base64Writer;
use encoding::Base64Reader;

fn main() {
    let args = os::args();
    let argc = args.len();

    if argc < 3 {
        return;
    }

    let writer = if argc > 3 {
        io::mk_file_writer(&Path(args[3]), ~[io::Create, io::Truncate]).get()
    } else {
        io::stdout()
    };

    match args[1] {
        ~"encode" => encode(&Path(args[2]), writer),
        ~"decode" => decode(&Path(args[2]), writer),
        _         => return
    }
}

fn encode(filename: &Path, writer: io::Writer) {
    let writer = &Base64Writer(BASE64, writer);
    let reader = io::file_reader(filename).get();

    let mut buf = [mut 0, ..1024];
    while !reader.eof() {
        let nread = reader.read(buf, buf.len());
        writer.write(vec::mut_view(buf, 0, nread));
    }

    // FIXME Remove this line once we get drop intrinsics.
    writer.close();
}

fn decode(filename: &Path, writer: io::Writer) {
    let reader = io::file_reader(filename).get();
    let reader = &Base64Reader(BASE64, reader);

    let mut buf = [mut 0, ..1024];
    while !reader.eof() {
        let nread = reader.read(buf, buf.len());
        writer.write(vec::mut_view(buf, 0, nread));
    }
}
<|endoftext|>