<commit_before>#![crate_name = "docopt_macros"]
#![crate_type = "dylib"]

#![feature(collections, core, plugin_registrar, quote, rustc_private)]

//! This crate defines the `docopt!` macro. It is documented in the
//! documentation of the `docopt` crate.

extern crate syntax;
extern crate rustc;
extern crate docopt;

use std::collections::HashMap;

use rustc::plugin::Registry;
use syntax::{ast, codemap};
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
use syntax::owned_slice::OwnedSlice;
use syntax::parse::common::SeqSep;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::util::small_vector::SmallVector;

use docopt::{Docopt, ArgvMap};
use docopt::parse::{Options, Atom, Positional, Zero, One};

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("docopt", expand);
}

fn expand(cx: &mut ExtCtxt, span: codemap::Span, tts: &[ast::TokenTree])
         -> Box<MacResult+'static> {
    let parsed = match MacParser::new(cx, tts).parse() {
        Ok(parsed) => parsed,
        Err(_) => return DummyResult::any(span),
    };
    parsed.items(cx)
}

/// Parsed corresponds to the result of parsing a `docopt` macro call.
/// It can be used to write a corresponding struct.
struct Parsed {
    struct_info: StructInfo,
    doc: Docopt,
    /// Overrided type annotations for struct members. May be empty.
    /// When a type annotation for an atom doesn't exist, then one is
    /// inferred automatically. It is one of: `bool`, `uint`, `String` or
    /// `Vec<String>`.
    types: HashMap<Atom, P<ast::Ty>>,
}

impl Parsed {
    /// Returns a macro result suitable for expansion.
    /// Contains two items: one for the struct and one for the struct impls.
    fn items(&self, cx: &ExtCtxt) -> Box<MacResult+'static> {
        let mut its = vec!();
        its.push(self.struct_decl(cx));

        let struct_name = self.struct_info.name;
        let full_doc = &*self.doc.parser().full_doc;
        its.push(quote_item!(cx,
            impl $struct_name {
                #[allow(dead_code)]
                fn docopt() -> docopt::Docopt {
                    // The unwrap is justified here because this code
                    // gen only happens if the Docopt usage string is valid.
                    docopt::Docopt::new($full_doc).unwrap()
                }
            }
        ).unwrap());
        MacEager::items(SmallVector::many(its))
    }

    /// Returns an item for the struct definition.
    fn struct_decl(&self, cx: &ExtCtxt) -> P<ast::Item> {
        let name = self.struct_info.name.clone();
        let vis = if self.struct_info.public { ast::Public }
                  else { ast::Inherited };
        let def = ast::StructDef {
            fields: self.struct_fields(cx),
            ctor_id: None
        };

        let mut traits = vec!["RustcDecodable".to_string()];
        traits.push_all(&*self.struct_info.deriving);
        let attrs = vec![attribute(cx, "allow", vec!["non_snake_case"]),
                         attribute(cx, "derive", traits)];

        let st = cx.item_struct(codemap::DUMMY_SP, name.clone(), def);
        cx.item(codemap::DUMMY_SP, name, attrs, st.node.clone()).map(|mut it| {
            it.vis = vis;
            it
        })
    }

    /// Returns a list of fields for the struct definition.
    /// Handles type annotations.
    fn struct_fields(&self, cx: &ExtCtxt) -> Vec<ast::StructField> {
        let mut fields: Vec<ast::StructField> = vec!();
        for (atom, opts) in self.doc.parser().descs.iter() {
            let name = ArgvMap::key_to_struct_field(&*atom.to_string());
            let ty = match self.types.get(atom) {
                None => self.pat_type(cx, atom, opts),
                Some(ty) => ty.clone(),
            };
            fields.push(self.mk_struct_field(&*name, ty));
        }
        fields
    }

    /// Returns an inferred type for a usage pattern.
    /// This is only invoked when a type annotation is not present.
    fn pat_type(&self, cx: &ExtCtxt, atom: &Atom, opts: &Options) -> P<ast::Ty> {
        let sp = codemap::DUMMY_SP;
        match (opts.repeats, &opts.arg) {
            (false, &Zero) => {
                match atom {
                    &Positional(_) => cx.ty_ident(sp, ident("String")),
                    _ => cx.ty_ident(sp, ident("bool")),
                }
            }
            (true, &Zero) => {
                match atom {
                    &Positional(_) => ty_vec_string(cx),
                    _ => cx.ty_ident(sp, ident("uint")),
                }
            }
            (false, &One(_)) => cx.ty_ident(sp, ident("String")),
            (true, &One(_)) => ty_vec_string(cx),
        }
    }

    /// Creates a struct field from a member name and type.
    fn mk_struct_field(&self, name: &str, ty: P<ast::Ty>) -> ast::StructField {
        codemap::dummy_spanned(ast::StructField_ {
            kind: ast::NamedField(ident(name), ast::Public),
            id: ast::DUMMY_NODE_ID,
            ty: ty,
            attrs: vec!(),
        })
    }
}

/// State for parsing a `docopt` macro invocation.
struct MacParser<'a, 'b:'a> {
    cx: &'a mut ExtCtxt<'b>,
    p: Parser<'b>,
}

impl<'a, 'b> MacParser<'a, 'b> {
    fn new(cx: &'a mut ExtCtxt<'b>, tts: &[ast::TokenTree]) -> MacParser<'a, 'b> {
        let p = cx.new_parser_from_tts(tts);
        MacParser { cx: cx, p: p }
    }

    /// Main entry point for parsing arguments to `docopt` macro.
    /// First looks for an identifier for the struct name.
    /// Second, a string containing the docopt usage patterns.
    /// Third, an optional list of type annotations.
    fn parse(&mut self) -> Result<Parsed, ()> {
        if self.p.token == token::Eof {
            self.cx.span_err(self.cx.call_site(), "macro expects arguments");
            return Err(());
        }
        let struct_info = try!(self.parse_struct_info());
        let docstr = try!(self.parse_str());

        let sep = SeqSep {
            sep: Some(token::Comma),
            trailing_sep_allowed: true,
        };
        let types = self.p.parse_seq_to_end(
            &token::Eof, sep, |p| MacParser::parse_type_annotation(p)
        ).into_iter()
         .map(|(ident, ty)| {
             let field_name = token::get_ident(ident).to_string();
             let key = ArgvMap::struct_field_to_key(&*field_name);
             (Atom::new(&*key), ty)
          })
         .collect::<HashMap<Atom, P<ast::Ty>>>();
        self.p.expect(&token::Eof);

        // This config does not matter because we're only asking for the
        // usage patterns in the Docopt string. The configuration does not
        // affect the retrieval of usage patterns.
        let doc = match Docopt::new(docstr) {
            Ok(doc) => doc,
            Err(err) => {
                self.cx.span_err(self.cx.call_site(),
                                 &*format!("Invalid Docopt usage: {}", err));
                return Err(());
            }
        };
        Ok(Parsed {
            struct_info: struct_info,
            doc: doc,
            types: types,
        })
    }

    /// Parses a single string literal. On failure, an error is logged and
    /// unit is returned.
    fn parse_str(&mut self) -> Result<String, ()> {
        fn lit_is_str(lit: &ast::Lit) -> bool {
            match lit.node {
                ast::LitStr(_, _) => true,
                _ => false,
            }
        }
        fn lit_to_string(lit: &ast::Lit) -> String {
            match lit.node {
                ast::LitStr(ref s, _) => s.to_string(),
                _ => panic!("BUG: expected string literal"),
            }
        }
        let exp = self.cx.expander().fold_expr(self.p.parse_expr());
        let s = match exp.node {
            ast::ExprLit(ref lit) if lit_is_str(&**lit) => {
                lit_to_string(&**lit)
            }
            _ => {
                let err = format!("Expected string literal but got {}",
                                  pprust::expr_to_string(&*exp));
                self.cx.span_err(exp.span, &*err);
                return Err(());
            }
        };
        self.p.bump();
        Ok(s)
    }

    /// Parses a type annotation in a `docopt` invocation of the form
    /// `ident: Ty`.
    /// Note that this is a static method as it is used as a HOF.
    fn parse_type_annotation(p: &mut Parser) -> (ast::Ident, P<ast::Ty>) {
        let ident = p.parse_ident();
        p.expect(&token::Colon);
        let ty = p.parse_ty();
        (ident, ty)
    }

    /// Parses struct information, like visibility, name and deriving.
    fn parse_struct_info(&mut self) -> Result<StructInfo, ()> {
        let public = self.p.eat_keyword(token::keywords::Pub);
        let mut info = StructInfo {
            name: self.p.parse_ident(),
            public: public,
            deriving: vec![],
        };
        if self.p.eat(&token::Comma) { return Ok(info); }
        let deriving = self.p.parse_ident();
        if deriving.as_str() != "derive" {
            let err = format!("Expected 'derive' keyword but got '{}'",
                              deriving);
            self.cx.span_err(self.cx.call_site(), &*err);
            return Err(());
        }
        while !self.p.eat(&token::Comma) {
            info.deriving.push(self.p.parse_ident().as_str().to_string());
        }
        Ok(info)
    }
}

struct StructInfo {
    name: ast::Ident,
    public: bool,
    deriving: Vec<String>,
}

// Convenience functions for building intermediate values.

fn ident(s: &str) -> ast::Ident {
    ast::Ident::new(token::intern(s))
}

fn attribute<S, T>(cx: &ExtCtxt, name: S, items: Vec<T>) -> ast::Attribute
            where S: Str, T: Str {
    let sp = codemap::DUMMY_SP;
    let its = items.into_iter().map(|s| meta_item(cx, s.as_slice())).collect();
    let mi = cx.meta_list(sp, intern(name.as_slice()), its);
    cx.attribute(sp, mi)
}

fn meta_item(cx: &ExtCtxt, s: &str) -> P<ast::MetaItem> {
    cx.meta_word(codemap::DUMMY_SP, intern(s))
}

fn intern(s: &str) -> token::InternedString {
    token::intern_and_get_ident(s)
}

fn ty_vec_string(cx: &ExtCtxt) -> P<ast::Ty> {
    let sp = codemap::DUMMY_SP;
    let tystr = ast::AngleBracketedParameterData {
        lifetimes: vec![],
        types: OwnedSlice::from_vec(vec![cx.ty_ident(sp, ident("String"))]),
        bindings: OwnedSlice::empty(),
    };
    cx.ty_path(ast::Path {
        span: sp,
        global: false,
        segments: vec![ast::PathSegment {
            identifier: ident("Vec"),
            parameters: ast::PathParameters::AngleBracketedParameters(tystr),
        }]
    })
}
<commit_msg>Use `usize` instead of deprecated `uint`.<commit_after>#![crate_name = "docopt_macros"]
#![crate_type = "dylib"]

#![feature(collections, core, plugin_registrar, quote, rustc_private)]

//! This crate defines the `docopt!` macro. It is documented in the
//! documentation of the `docopt` crate.

extern crate syntax;
extern crate rustc;
extern crate docopt;

use std::collections::HashMap;

use rustc::plugin::Registry;
use syntax::{ast, codemap};
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use syntax::ext::build::AstBuilder;
use syntax::fold::Folder;
use syntax::owned_slice::OwnedSlice;
use syntax::parse::common::SeqSep;
use syntax::parse::parser::Parser;
use syntax::parse::token;
use syntax::print::pprust;
use syntax::ptr::P;
use syntax::util::small_vector::SmallVector;

use docopt::{Docopt, ArgvMap};
use docopt::parse::{Options, Atom, Positional, Zero, One};

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("docopt", expand);
}

fn expand(cx: &mut ExtCtxt, span: codemap::Span, tts: &[ast::TokenTree])
         -> Box<MacResult+'static> {
    let parsed = match MacParser::new(cx, tts).parse() {
        Ok(parsed) => parsed,
        Err(_) => return DummyResult::any(span),
    };
    parsed.items(cx)
}

/// Parsed corresponds to the result of parsing a `docopt` macro call.
/// It can be used to write a corresponding struct.
struct Parsed {
    struct_info: StructInfo,
    doc: Docopt,
    /// Overrided type annotations for struct members. May be empty.
    /// When a type annotation for an atom doesn't exist, then one is
    /// inferred automatically. It is one of: `bool`, `usize`, `String` or
    /// `Vec<String>`.
    types: HashMap<Atom, P<ast::Ty>>,
}

impl Parsed {
    /// Returns a macro result suitable for expansion.
    /// Contains two items: one for the struct and one for the struct impls.
    fn items(&self, cx: &ExtCtxt) -> Box<MacResult+'static> {
        let mut its = vec!();
        its.push(self.struct_decl(cx));

        let struct_name = self.struct_info.name;
        let full_doc = &*self.doc.parser().full_doc;
        its.push(quote_item!(cx,
            impl $struct_name {
                #[allow(dead_code)]
                fn docopt() -> docopt::Docopt {
                    // The unwrap is justified here because this code
                    // gen only happens if the Docopt usage string is valid.
                    docopt::Docopt::new($full_doc).unwrap()
                }
            }
        ).unwrap());
        MacEager::items(SmallVector::many(its))
    }

    /// Returns an item for the struct definition.
    fn struct_decl(&self, cx: &ExtCtxt) -> P<ast::Item> {
        let name = self.struct_info.name.clone();
        let vis = if self.struct_info.public { ast::Public }
                  else { ast::Inherited };
        let def = ast::StructDef {
            fields: self.struct_fields(cx),
            ctor_id: None
        };

        let mut traits = vec!["RustcDecodable".to_string()];
        traits.push_all(&*self.struct_info.deriving);
        let attrs = vec![attribute(cx, "allow", vec!["non_snake_case"]),
                         attribute(cx, "derive", traits)];

        let st = cx.item_struct(codemap::DUMMY_SP, name.clone(), def);
        cx.item(codemap::DUMMY_SP, name, attrs, st.node.clone()).map(|mut it| {
            it.vis = vis;
            it
        })
    }

    /// Returns a list of fields for the struct definition.
    /// Handles type annotations.
    fn struct_fields(&self, cx: &ExtCtxt) -> Vec<ast::StructField> {
        let mut fields: Vec<ast::StructField> = vec!();
        for (atom, opts) in self.doc.parser().descs.iter() {
            let name = ArgvMap::key_to_struct_field(&*atom.to_string());
            let ty = match self.types.get(atom) {
                None => self.pat_type(cx, atom, opts),
                Some(ty) => ty.clone(),
            };
            fields.push(self.mk_struct_field(&*name, ty));
        }
        fields
    }

    /// Returns an inferred type for a usage pattern.
    /// This is only invoked when a type annotation is not present.
    fn pat_type(&self, cx: &ExtCtxt, atom: &Atom, opts: &Options) -> P<ast::Ty> {
        let sp = codemap::DUMMY_SP;
        match (opts.repeats, &opts.arg) {
            (false, &Zero) => {
                match atom {
                    &Positional(_) => cx.ty_ident(sp, ident("String")),
                    _ => cx.ty_ident(sp, ident("bool")),
                }
            }
            (true, &Zero) => {
                match atom {
                    &Positional(_) => ty_vec_string(cx),
                    _ => cx.ty_ident(sp, ident("usize")),
                }
            }
            (false, &One(_)) => cx.ty_ident(sp, ident("String")),
            (true, &One(_)) => ty_vec_string(cx),
        }
    }

    /// Creates a struct field from a member name and type.
    fn mk_struct_field(&self, name: &str, ty: P<ast::Ty>) -> ast::StructField {
        codemap::dummy_spanned(ast::StructField_ {
            kind: ast::NamedField(ident(name), ast::Public),
            id: ast::DUMMY_NODE_ID,
            ty: ty,
            attrs: vec!(),
        })
    }
}

/// State for parsing a `docopt` macro invocation.
struct MacParser<'a, 'b:'a> {
    cx: &'a mut ExtCtxt<'b>,
    p: Parser<'b>,
}

impl<'a, 'b> MacParser<'a, 'b> {
    fn new(cx: &'a mut ExtCtxt<'b>, tts: &[ast::TokenTree]) -> MacParser<'a, 'b> {
        let p = cx.new_parser_from_tts(tts);
        MacParser { cx: cx, p: p }
    }

    /// Main entry point for parsing arguments to `docopt` macro.
    /// First looks for an identifier for the struct name.
    /// Second, a string containing the docopt usage patterns.
    /// Third, an optional list of type annotations.
    fn parse(&mut self) -> Result<Parsed, ()> {
        if self.p.token == token::Eof {
            self.cx.span_err(self.cx.call_site(), "macro expects arguments");
            return Err(());
        }
        let struct_info = try!(self.parse_struct_info());
        let docstr = try!(self.parse_str());

        let sep = SeqSep {
            sep: Some(token::Comma),
            trailing_sep_allowed: true,
        };
        let types = self.p.parse_seq_to_end(
            &token::Eof, sep, |p| MacParser::parse_type_annotation(p)
        ).into_iter()
         .map(|(ident, ty)| {
             let field_name = token::get_ident(ident).to_string();
             let key = ArgvMap::struct_field_to_key(&*field_name);
             (Atom::new(&*key), ty)
          })
         .collect::<HashMap<Atom, P<ast::Ty>>>();
        self.p.expect(&token::Eof);

        // This config does not matter because we're only asking for the
        // usage patterns in the Docopt string. The configuration does not
        // affect the retrieval of usage patterns.
        let doc = match Docopt::new(docstr) {
            Ok(doc) => doc,
            Err(err) => {
                self.cx.span_err(self.cx.call_site(),
                                 &*format!("Invalid Docopt usage: {}", err));
                return Err(());
            }
        };
        Ok(Parsed {
            struct_info: struct_info,
            doc: doc,
            types: types,
        })
    }

    /// Parses a single string literal. On failure, an error is logged and
    /// unit is returned.
    fn parse_str(&mut self) -> Result<String, ()> {
        fn lit_is_str(lit: &ast::Lit) -> bool {
            match lit.node {
                ast::LitStr(_, _) => true,
                _ => false,
            }
        }
        fn lit_to_string(lit: &ast::Lit) -> String {
            match lit.node {
                ast::LitStr(ref s, _) => s.to_string(),
                _ => panic!("BUG: expected string literal"),
            }
        }
        let exp = self.cx.expander().fold_expr(self.p.parse_expr());
        let s = match exp.node {
            ast::ExprLit(ref lit) if lit_is_str(&**lit) => {
                lit_to_string(&**lit)
            }
            _ => {
                let err = format!("Expected string literal but got {}",
                                  pprust::expr_to_string(&*exp));
                self.cx.span_err(exp.span, &*err);
                return Err(());
            }
        };
        self.p.bump();
        Ok(s)
    }

    /// Parses a type annotation in a `docopt` invocation of the form
    /// `ident: Ty`.
    /// Note that this is a static method as it is used as a HOF.
    fn parse_type_annotation(p: &mut Parser) -> (ast::Ident, P<ast::Ty>) {
        let ident = p.parse_ident();
        p.expect(&token::Colon);
        let ty = p.parse_ty();
        (ident, ty)
    }

    /// Parses struct information, like visibility, name and deriving.
    fn parse_struct_info(&mut self) -> Result<StructInfo, ()> {
        let public = self.p.eat_keyword(token::keywords::Pub);
        let mut info = StructInfo {
            name: self.p.parse_ident(),
            public: public,
            deriving: vec![],
        };
        if self.p.eat(&token::Comma) { return Ok(info); }
        let deriving = self.p.parse_ident();
        if deriving.as_str() != "derive" {
            let err = format!("Expected 'derive' keyword but got '{}'",
                              deriving);
            self.cx.span_err(self.cx.call_site(), &*err);
            return Err(());
        }
        while !self.p.eat(&token::Comma) {
            info.deriving.push(self.p.parse_ident().as_str().to_string());
        }
        Ok(info)
    }
}

struct StructInfo {
    name: ast::Ident,
    public: bool,
    deriving: Vec<String>,
}

// Convenience functions for building intermediate values.

fn ident(s: &str) -> ast::Ident {
    ast::Ident::new(token::intern(s))
}

fn attribute<S, T>(cx: &ExtCtxt, name: S, items: Vec<T>) -> ast::Attribute
            where S: Str, T: Str {
    let sp = codemap::DUMMY_SP;
    let its = items.into_iter().map(|s| meta_item(cx, s.as_slice())).collect();
    let mi = cx.meta_list(sp, intern(name.as_slice()), its);
    cx.attribute(sp, mi)
}

fn meta_item(cx: &ExtCtxt, s: &str) -> P<ast::MetaItem> {
    cx.meta_word(codemap::DUMMY_SP, intern(s))
}

fn intern(s: &str) -> token::InternedString {
    token::intern_and_get_ident(s)
}

fn ty_vec_string(cx: &ExtCtxt) -> P<ast::Ty> {
    let sp = codemap::DUMMY_SP;
    let tystr = ast::AngleBracketedParameterData {
        lifetimes: vec![],
        types: OwnedSlice::from_vec(vec![cx.ty_ident(sp, ident("String"))]),
        bindings: OwnedSlice::empty(),
    };
    cx.ty_path(ast::Path {
        span: sp,
        global: false,
        segments: vec![ast::PathSegment {
            identifier: ident("Vec"),
            parameters: ast::PathParameters::AngleBracketedParameters(tystr),
        }]
    })
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;

use std::{io, fs};
use std::io::BufRead;
use std::iter::FromIterator;
use std::collections::HashMap;
use std::error::Error;
use fst::{IntoStreamer, Streamer, Set};
use rand::thread_rng;
use rand::seq::SliceRandom;

const HIRAGANAS: [char; 81] = ['ぁ', 'あ', 'ぃ', 'い', 'ぅ', 'う', 'ぇ', 'え', 'ぉ', 'お', 'か', 'が', 'き', 'ぎ', 'く', 'ぐ', 'け', 'げ', 'こ', 'ご', 'さ', 'ざ', 'し', 'じ', 'す', 'ず', 'せ', 'ぜ', 'そ', 'ぞ', 'た', 'だ', 'ち', 'ぢ', 'っ', 'つ', 'づ', 'て', 'で', 'と', 'ど', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ば', 'ぱ', 'ひ', 'び', 'ぴ', 'ふ', 'ぶ', 'ぷ', 'へ', 'べ', 'ぺ', 'ほ', 'ぼ', 'ぽ', 'ま', 'み', 'む', 'め', 'も', 'ゃ', 'や', 'ゅ', 'ゆ', 'ょ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ', 'わ', 'を', 'ん', 'ー'];
lazy_static! {
    static ref NON_INITIALS: Vec<u8> = {
        (0..HIRAGANAS.len()).filter_map(|index| {
            if "ぁぃぅぇぉっゃゅょぢづーをん".contains(HIRAGANAS[index]) {
                Some(index as u8 + 1)
            } else {
                None
            }
        }).collect()
    };
}

struct Context {
    constraints: Vec<Vec<usize>>,
    set: Set,
    one_gram_counter: HashMap<[u8; 1], usize>,
    two_gram_counter: HashMap<[u8; 2], usize>,
}

fn is_non_initial(c: u8) -> bool {
    (*NON_INITIALS).contains(&c)
}

fn bytes_to_string(bytes: Vec<u8>) -> String {
    String::from_iter(bytes.iter().map(|&i| {
        if i == 0 {
            '　'
        } else {
            HIRAGANAS[(i - 1) as usize]
        }
    }))
}

fn has_prefix(set: &Set, prefix: Vec<u8>) -> bool {
    let mut prefix_stop = prefix.clone();
    *prefix_stop.last_mut().unwrap() += 1;
    set.range().ge(prefix).lt(prefix_stop).into_stream().next() != None
}

fn get_prefix(set: &Set, prefix: Vec<u8>) -> Vec<Vec<u8>> {
    let mut prefix_stop = prefix.clone();
    *prefix_stop.last_mut().unwrap() += 1;
    set.range().ge(prefix).lt(prefix_stop).into_stream().into_bytes()
}

fn count_prefix(set: &Set, prefix: Vec<u8>) -> usize {
    let mut prefix_stop = prefix.clone();
    *prefix_stop.last_mut().unwrap() += 1;
    set.range().ge(prefix).lt(prefix_stop).into_stream().into_bytes().len()
}

fn get_board(context: &Context, board: [u8; 16]) -> Option<[u8; 16]> {
    let mut new_values: Vec<u8> = (1..(HIRAGANAS.len() as u8 + 1)).collect();
    let mut rng = thread_rng();
    new_values.shuffle(&mut rng);
    let index = match board.iter().position(|&c| c == 0) {
        Some(i) => i,
        None => {
            return Some(board);
        },
    };
    'values: for new_value in new_values {
        if is_non_initial(new_value) {
            continue;
        }

        let mut cloned_board = board.clone();
        cloned_board[index] = new_value;

        // check if constraints are met
        for constraint in context.constraints.iter() {
            let prefix: Vec<u8> = constraint.iter().map(|&c| board[c]).take_while(|&c| c != 0).collect();
            if prefix.len() == 0 {
                continue;
            }
            if !has_prefix(&context.set, prefix) {
                continue 'values;
            }
        }

        let result = get_board(context, cloned_board);
        if let Some(board) = result {
            return Some(board);
        }
    }
    None
}

fn main() -> Result<(), Box<dyn Error>> {
    let f = fs::File::open("crossword.txt")?;
    let file = io::BufReader::new(f);

    let mut words: Vec<Vec<u8>> = Vec::new();
    'lines: for line in file.lines() {
        let word = line?;
        let mut indices: Vec<u8> = Vec::new();
        for char in word.chars() {
            match HIRAGANAS.iter().position(|&c| c == char) {
                Some(i) => indices.push((i + 1) as u8),
                None => continue 'lines,
            }
        }
        words.push(indices);
    }

    words.sort();
    let set = Set::from_iter(words)?;

    let mut one_gram_counter: HashMap<[u8; 1], usize> = HashMap::new();
    let mut two_gram_counter: HashMap<[u8; 2], usize> = HashMap::new();

    for i in 0..HIRAGANAS.len() {
        one_gram_counter.insert([i as u8], count_prefix(&set, vec![i as u8]));
        for j in 0..HIRAGANAS.len() {
            two_gram_counter.insert([i as u8, j as u8], count_prefix(&set, vec![i as u8, j as u8]));
        }
    }

    let constraints: Vec<Vec<usize>> = vec![
        vec![0, 1, 2, 3],
        vec![4, 5, 6, 7],
        vec![8, 9, 10, 11],
        vec![12, 13, 14, 15],
        vec![0, 5, 10, 15],
        vec![3, 7, 11, 15],
        vec![2, 6, 10, 14],
        vec![1, 5, 9, 13],
        vec![0, 4, 8, 12],
    ];

    let board = get_board(&Context {
        set: set,
        constraints: constraints,
        one_gram_counter: one_gram_counter,
        two_gram_counter: two_gram_counter,
    }, [0; 16]);
    if let Some(board) = board {
        println!("{:?}", bytes_to_string(board.to_vec()));
    }

    Ok(())
}
<commit_msg>Use left-top cell as predicate in get_board method<commit_after>#[macro_use]
extern crate lazy_static;

use std::{io, fs};
use std::io::BufRead;
use std::iter::FromIterator;
use std::collections::HashMap;
use std::error::Error;
use fst::{IntoStreamer, Streamer, Set};
use rand::thread_rng;
use rand::seq::SliceRandom;

const HIRAGANAS: [char; 81] = ['ぁ', 'あ', 'ぃ', 'い', 'ぅ', 'う', 'ぇ', 'え', 'ぉ', 'お', 'か', 'が', 'き', 'ぎ', 'く', 'ぐ', 'け', 'げ', 'こ', 'ご', 'さ', 'ざ', 'し', 'じ', 'す', 'ず', 'せ', 'ぜ', 'そ', 'ぞ', 'た', 'だ', 'ち', 'ぢ', 'っ', 'つ', 'づ', 'て', 'で', 'と', 'ど', 'な', 'に', 'ぬ', 'ね', 'の', 'は', 'ば', 'ぱ', 'ひ', 'び', 'ぴ', 'ふ', 'ぶ', 'ぷ', 'へ', 'べ', 'ぺ', 'ほ', 'ぼ', 'ぽ', 'ま', 'み', 'む', 'め', 'も', 'ゃ', 'や', 'ゅ', 'ゆ', 'ょ', 'よ', 'ら', 'り', 'る', 'れ', 'ろ', 'わ', 'を', 'ん', 'ー'];
lazy_static! {
    static ref NON_INITIALS: Vec<u8> = {
        (0..HIRAGANAS.len()).filter_map(|index| {
            if "ぁぃぅぇぉっゃゅょぢづーをん".contains(HIRAGANAS[index]) {
                Some(index as u8 + 1)
            } else {
                None
            }
        }).collect()
    };
}

struct Context {
    constraints: Vec<Vec<usize>>,
    set: Set,
    one_gram_counter: HashMap<[u8; 1], usize>,
    two_gram_counter: HashMap<[u8; 2], usize>,
}

fn is_non_initial(c: u8) -> bool {
    (*NON_INITIALS).contains(&c)
}

fn bytes_to_string(bytes: Vec<u8>) -> String {
    String::from_iter(bytes.iter().map(|&i| {
        if i == 0 {
            '　'
        } else {
            HIRAGANAS[(i - 1) as usize]
        }
    }))
}

fn has_prefix(set: &Set, prefix: Vec<u8>) -> bool {
    let mut prefix_stop = prefix.clone();
    *prefix_stop.last_mut().unwrap() += 1;
    set.range().ge(prefix).lt(prefix_stop).into_stream().next() != None
}

fn get_prefix(set: &Set, prefix: Vec<u8>) -> Vec<Vec<u8>> {
    let mut prefix_stop = prefix.clone();
    *prefix_stop.last_mut().unwrap() += 1;
    set.range().ge(prefix).lt(prefix_stop).into_stream().into_bytes()
}

fn count_prefix(set: &Set, prefix: Vec<u8>) -> usize {
    let mut prefix_stop = prefix.clone();
    *prefix_stop.last_mut().unwrap() += 1;
    set.range().ge(prefix).lt(prefix_stop).into_stream().into_bytes().len()
}

fn get_board(context: &Context, board: [u8; 16]) -> Option<[u8; 16]> {
    let mut new_values: Vec<u8> = (1..(HIRAGANAS.len() as u8 + 1)).collect();
    let mut rng = thread_rng();
    new_values.shuffle(&mut rng);
    if board.iter().all(|&c| c != 0) {
        return Some(board);
    }

    let index = board.iter().enumerate().min_by_key(|(i, &c)| {
        if c != 0 {
            return std::u8::MAX;
        }
        let x = *i as u8 % 4;
        let y = *i as u8 / 4;
        return x + y;
    }).unwrap().0;

    'values: for new_value in new_values {
        if is_non_initial(new_value) {
            continue;
        }

        let mut cloned_board = board.clone();
        cloned_board[index] = new_value;

        // check if constraints are met
        for constraint in context.constraints.iter() {
            let prefix: Vec<u8> = constraint.iter().map(|&c| board[c]).take_while(|&c| c != 0).collect();
            if prefix.len() == 0 {
                continue;
            }
            if !has_prefix(&context.set, prefix) {
                continue 'values;
            }
        }

        let result = get_board(context, cloned_board);
        if let Some(board) = result {
            return Some(board);
        }
    }
    None
}

fn main() -> Result<(), Box<dyn Error>> {
    let f = fs::File::open("crossword.txt")?;
    let file = io::BufReader::new(f);

    let mut words: Vec<Vec<u8>> = Vec::new();
    'lines: for line in file.lines() {
        let word = line?;
        let mut indices: Vec<u8> = Vec::new();
        for char in word.chars() {
            match HIRAGANAS.iter().position(|&c| c == char) {
                Some(i) => indices.push((i + 1) as u8),
                None => continue 'lines,
            }
        }
        words.push(indices);
    }

    words.sort();
    let set = Set::from_iter(words)?;

    let mut one_gram_counter: HashMap<[u8; 1], usize> = HashMap::new();
    let mut two_gram_counter: HashMap<[u8; 2], usize> = HashMap::new();

    for i in 0..HIRAGANAS.len() {
        one_gram_counter.insert([i as u8], count_prefix(&set, vec![i as u8]));
        for j in 0..HIRAGANAS.len() {
            two_gram_counter.insert([i as u8, j as u8], count_prefix(&set, vec![i as u8, j as u8]));
        }
    }

    let constraints: Vec<Vec<usize>> = vec![
        vec![0, 1, 2, 3],
        vec![4, 5, 6, 7],
        vec![8, 9, 10, 11],
        vec![12, 13, 14, 15],
        vec![0, 5, 10, 15],
        vec![3, 7, 11, 15],
        vec![2, 6, 10, 14],
        vec![1, 5, 9, 13],
        vec![0, 4, 8, 12],
    ];

    let board = get_board(&Context {
        set: set,
        constraints: constraints,
        one_gram_counter: one_gram_counter,
        two_gram_counter: two_gram_counter,
    }, [0; 16]);
    if let Some(board) = board {
        println!("{:?}", bytes_to_string(board.to_vec()));
    }

    Ok(())
}
<|endoftext|>


<commit_before>// This example uses get_sock() to find out which file descriptors c-ares wants us to listen on,
// and uses epoll() to satisfy those requirements.

#[cfg(all(unix, any(target_os = "linux", target_os = "android")))]
extern crate nix;

#[cfg(all(unix, any(target_os = "linux", target_os = "android")))]
mod example {
    extern crate c_ares;
    use nix::sys::epoll::{epoll_create, epoll_ctl, epoll_wait, EpollEvent, EpollFlags, EpollOp};
    use std::collections::HashSet;

    fn print_a_results(result: &c_ares::Result<c_ares::AResults>) {
        match *result {
            Err(ref e) => {
                println!("A lookup failed with error '{}'", e);
            }
            Ok(ref a_results) => {
                println!("Successful A lookup...");
                for a_result in a_results {
                    println!("IPv4: {}", a_result.ipv4());
                    println!("TTL: {}", a_result.ttl());
                }
            }
        }
    }

    fn print_aaaa_results(result: &c_ares::Result<c_ares::AAAAResults>) {
        match *result {
            Err(ref e) => {
                println!("AAAA lookup failed with error '{}'", e);
            }
            Ok(ref aaaa_results) => {
                println!("Successful AAAA lookup...");
                for aaaa_result in aaaa_results {
                    println!("IPv6: {}", aaaa_result.ipv6());
                    println!("TTL: {}", aaaa_result.ttl());
                }
            }
        }
    }

    fn print_caa_results(result: &c_ares::Result<c_ares::CAAResults>) {
        match *result {
            Err(ref e) => {
                println!("CAA lookup failed with error '{}'", e);
            }
            Ok(ref caa_results) => {
                println!("Successful CAA lookup...");
                for caa_result in caa_results {
                    println!(
                        "critical: {}, property: {}, value: {}",
                        caa_result.critical(),
                        caa_result.property().to_string_lossy(),
                        caa_result.value().to_string_lossy()
                    );
                }
            }
        }
    }

    fn print_srv_results(result: &c_ares::Result<c_ares::SRVResults>) {
        match *result {
            Err(ref e) => {
                println!("SRV lookup failed with error '{}'", e);
            }
            Ok(ref srv_results) => {
                println!("Successful SRV lookup...");
                for srv_result in srv_results {
                    println!(
                        "host: {} (port: {}), priority: {} weight: {}",
                        srv_result.host().to_string_lossy(),
                        srv_result.port(),
                        srv_result.priority(),
                        srv_result.weight()
                    );
                }
            }
        }
    }

    pub fn main() {
        // Create the c_ares::Channel.
        let mut options = c_ares::Options::new();
        options
            .set_flags(c_ares::Flags::STAYOPEN)
            .set_timeout(500)
            .set_tries(3);
        let mut ares_channel =
            c_ares::Channel::with_options(options).expect("Failed to create channel");
        ares_channel
            .set_servers(&["8.8.8.8"])
            .expect("Failed to set servers");

        // Set up some queries.
        ares_channel.query_a("apple.com", move |result| {
            println!();
            print_a_results(&result);
        });

        ares_channel.query_aaaa("google.com", move |result| {
            println!();
            print_aaaa_results(&result);
        });

        ares_channel.query_caa("comodo.com", move |result| {
            println!();
            print_caa_results(&result);
        });

        ares_channel.query_srv("_xmpp-server._tcp.gmail.com", move |result| {
            println!();
            print_srv_results(&result);
        });

        // Create an epoll file descriptor so that we can listen for events.
        let epoll = epoll_create().expect("Failed to create epoll");
        let mut tracked_fds = HashSet::<c_ares::Socket>::new();
        loop {
            // Ask c-ares what file descriptors we should be listening on, and map those requests
            // onto the epoll file descriptor.
            let mut new_tracked_fds = HashSet::new();
            for (fd, readable, writable) in &ares_channel.get_sock() {
                let mut interest = EpollFlags::empty();
                if readable {
                    interest |= EpollFlags::EPOLLIN;
                }
                if writable {
                    interest |= EpollFlags::EPOLLOUT;
                }
                let mut event = EpollEvent::new(interest, fd as u64);

                // Anything left over in tracked_fds when we exit the loop is no longer wanted by
                // c-ares.
                let op = if tracked_fds.remove(&fd) {
                    EpollOp::EpollCtlMod
                } else {
                    EpollOp::EpollCtlAdd
                };
                new_tracked_fds.insert(fd);

                epoll_ctl(epoll, op, fd, &mut event).expect("epoll_ctl failed");
            }

            // Stop listening for events on file descriptors that c-ares doesn't care about.
            for fd in &tracked_fds {
                epoll_ctl(epoll, EpollOp::EpollCtlDel, *fd, None).expect("epoll_ctl failed");
            }
            tracked_fds = new_tracked_fds;

            // If c-ares isn't asking us to do anything, we're done.
            if tracked_fds.is_empty() {
                break;
            }

            // Wait for something to happen.
            let empty_event = EpollEvent::new(EpollFlags::empty(), 0);
            let mut events = [empty_event; 2];
            let results = epoll_wait(epoll, &mut events, 500).expect("epoll_wait failed");

            // Process whatever happened.
            match results {
                0 => {
                    // No events - must be a timeout.  Tell c-ares about it.
                    ares_channel.process_fd(c_ares::SOCKET_BAD, c_ares::SOCKET_BAD);
                }
                n => {
                    // Sockets became readable or writable.  Tell c-ares.
                    for event in &events[0..n] {
                        let active_fd = event.data() as c_ares::Socket;
                        let rfd = if (event.events() & EpollFlags::EPOLLIN).is_empty() {
                            c_ares::SOCKET_BAD
                        } else {
                            active_fd
                        };
                        let wfd = if (event.events() & EpollFlags::EPOLLOUT).is_empty() {
                            c_ares::SOCKET_BAD
                        } else {
                            active_fd
                        };
                        ares_channel.process_fd(rfd, wfd);
                    }
                }
            }
        }
    }
}

#[cfg(all(unix, any(target_os = "linux", target_os = "android")))]
pub fn main() {
    example::main();
}

#[cfg(not(all(unix, any(target_os = "linux", target_os = "android"))))]
pub fn main() {
    println!("this example is not supported on this platform");
}
<commit_msg>working srv record example<commit_after>// This example uses get_sock() to find out which file descriptors c-ares wants us to listen on,
// and uses epoll() to satisfy those requirements.

#[cfg(all(unix, any(target_os = "linux", target_os = "android")))]
extern crate nix;

#[cfg(all(unix, any(target_os = "linux", target_os = "android")))]
mod example {
    extern crate c_ares;
    use nix::sys::epoll::{epoll_create, epoll_ctl, epoll_wait, EpollEvent, EpollFlags, EpollOp};
    use std::collections::HashSet;

    fn print_a_results(result: &c_ares::Result<c_ares::AResults>) {
        match *result {
            Err(ref e) => {
                println!("A lookup failed with error '{}'", e);
            }
            Ok(ref a_results) => {
                println!("Successful A lookup...");
                for a_result in a_results {
                    println!("IPv4: {}", a_result.ipv4());
                    println!("TTL: {}", a_result.ttl());
                }
            }
        }
    }

    fn print_aaaa_results(result: &c_ares::Result<c_ares::AAAAResults>) {
        match *result {
            Err(ref e) => {
                println!("AAAA lookup failed with error '{}'", e);
            }
            Ok(ref aaaa_results) => {
                println!("Successful AAAA lookup...");
                for aaaa_result in aaaa_results {
                    println!("IPv6: {}", aaaa_result.ipv6());
                    println!("TTL: {}", aaaa_result.ttl());
                }
            }
        }
    }

    fn print_caa_results(result: &c_ares::Result<c_ares::CAAResults>) {
        match *result {
            Err(ref e) => {
                println!("CAA lookup failed with error '{}'", e);
            }
            Ok(ref caa_results) => {
                println!("Successful CAA lookup...");
                for caa_result in caa_results {
                    println!(
                        "critical: {}, property: {}, value: {}",
                        caa_result.critical(),
                        caa_result.property().to_string_lossy(),
                        caa_result.value().to_string_lossy()
                    );
                }
            }
        }
    }

    fn print_srv_results(result: &c_ares::Result<c_ares::SRVResults>) {
        match *result {
            Err(ref e) => {
                println!("SRV lookup failed with error '{}'", e);
            }
            Ok(ref srv_results) => {
                println!("Successful SRV lookup...");
                for srv_result in srv_results {
                    println!(
                        "host: {} (port: {}), priority: {} weight: {}",
                        srv_result.host().to_string_lossy(),
                        srv_result.port(),
                        srv_result.priority(),
                        srv_result.weight()
                    );
                }
            }
        }
    }

    pub fn main() {
        // Create the c_ares::Channel.
        let mut options = c_ares::Options::new();
        options
            .set_flags(c_ares::Flags::STAYOPEN)
            .set_timeout(500)
            .set_tries(3);
        let mut ares_channel =
            c_ares::Channel::with_options(options).expect("Failed to create channel");
        ares_channel
            .set_servers(&["8.8.8.8"])
            .expect("Failed to set servers");

        // Set up some queries.
        ares_channel.query_a("apple.com", move |result| {
            println!();
            print_a_results(&result);
        });

        ares_channel.query_aaaa("google.com", move |result| {
            println!();
            print_aaaa_results(&result);
        });

        ares_channel.query_caa("comodo.com", move |result| {
            println!();
            print_caa_results(&result);
        });

        ares_channel.query_srv("_xmpp-server._tcp.jabber.org", move |result| {
            println!();
            print_srv_results(&result);
        });

        // Create an epoll file descriptor so that we can listen for events.
        let epoll = epoll_create().expect("Failed to create epoll");
        let mut tracked_fds = HashSet::<c_ares::Socket>::new();
        loop {
            // Ask c-ares what file descriptors we should be listening on, and map those requests
            // onto the epoll file descriptor.
            let mut new_tracked_fds = HashSet::new();
            for (fd, readable, writable) in &ares_channel.get_sock() {
                let mut interest = EpollFlags::empty();
                if readable {
                    interest |= EpollFlags::EPOLLIN;
                }
                if writable {
                    interest |= EpollFlags::EPOLLOUT;
                }
                let mut event = EpollEvent::new(interest, fd as u64);

                // Anything left over in tracked_fds when we exit the loop is no longer wanted by
                // c-ares.
                let op = if tracked_fds.remove(&fd) {
                    EpollOp::EpollCtlMod
                } else {
                    EpollOp::EpollCtlAdd
                };
                new_tracked_fds.insert(fd);

                epoll_ctl(epoll, op, fd, &mut event).expect("epoll_ctl failed");
            }

            // Stop listening for events on file descriptors that c-ares doesn't care about.
            for fd in &tracked_fds {
                epoll_ctl(epoll, EpollOp::EpollCtlDel, *fd, None).expect("epoll_ctl failed");
            }
            tracked_fds = new_tracked_fds;

            // If c-ares isn't asking us to do anything, we're done.
            if tracked_fds.is_empty() {
                break;
            }

            // Wait for something to happen.
            let empty_event = EpollEvent::new(EpollFlags::empty(), 0);
            let mut events = [empty_event; 2];
            let results = epoll_wait(epoll, &mut events, 500).expect("epoll_wait failed");

            // Process whatever happened.
            match results {
                0 => {
                    // No events - must be a timeout.  Tell c-ares about it.
                    ares_channel.process_fd(c_ares::SOCKET_BAD, c_ares::SOCKET_BAD);
                }
                n => {
                    // Sockets became readable or writable.  Tell c-ares.
                    for event in &events[0..n] {
                        let active_fd = event.data() as c_ares::Socket;
                        let rfd = if (event.events() & EpollFlags::EPOLLIN).is_empty() {
                            c_ares::SOCKET_BAD
                        } else {
                            active_fd
                        };
                        let wfd = if (event.events() & EpollFlags::EPOLLOUT).is_empty() {
                            c_ares::SOCKET_BAD
                        } else {
                            active_fd
                        };
                        ares_channel.process_fd(rfd, wfd);
                    }
                }
            }
        }
    }
}

#[cfg(all(unix, any(target_os = "linux", target_os = "android")))]
pub fn main() {
    example::main();
}

#[cfg(not(all(unix, any(target_os = "linux", target_os = "android"))))]
pub fn main() {
    println!("this example is not supported on this platform");
}
<|endoftext|>


<commit_before>#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate ease;

use std::collections::HashMap;
use ease::RestClient;

#[derive(Deserialize, Debug)]
struct Response {
    args: HashMap<String, String>,
    headers: HashMap<String, String>,
    origin: String,
    url: String
}

fn main() {
    println!("{:?}",
             RestClient::new("http://httpbin.org/get")
                        .ok().expect("Couldn't parse URL.")
                        .get_json_as::<Response>()
                        .unwrap()
        );
}
<commit_msg>Add JSON and POST example.<commit_after>#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate serde;
extern crate ease;

use std::collections::HashMap;
use ease::RestClient;

#[derive(Deserialize, Debug)]
struct Response {
    args: HashMap<String, String>,
    data: Option<String>,
    files: HashMap<String, String>,
    form: HashMap<String, String>,
    headers: HashMap<String, String>,
    json: Option<String>,
    origin: String,
    url: String
}

fn main() {
    println!("{:?}",
             RestClient::new("http://httpbin.org/post")
                        .ok().expect("Couldn't parse URL.")
                        .post_json_as::<Response>()
        );

    println!("{:?}",
             RestClient::new("http://httpbin.org/get")
                        .ok().expect("Couldn't parse URL.")
                        .get_json_as::<Response>()
        );
}
<|endoftext|>


<commit_before>extern crate termion;
extern crate tui;

use std::io;
use std::thread;
use std::time;
use std::sync::mpsc;

use termion::event;
use termion::input::TermRead;

use tui::Terminal;
use tui::backend::MouseBackend;
use tui::widgets::{Block, Borders, Item, List, SelectableList, Widget};
use tui::layout::{Direction, Group, Rect, Size};
use tui::style::{Color, Modifier, Style};

struct App<'a> {
    size: Rect,
    items: Vec<&'a str>,
    selected: usize,
    events: Vec<(&'a str, &'a str)>,
    info_style: Style,
    warning_style: Style,
    error_style: Style,
    critical_style: Style,
}

impl<'a> App<'a> {
    fn new() -> App<'a> {
        App {
            size: Rect::default(),
            items: vec![
                "Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9",
                "Item10", "Item11", "Item12", "Item13", "Item14", "Item15", "Item16", "Item17",
                "Item18", "Item19", "Item20", "Item21", "Item22", "Item23", "Item24",
            ],
            selected: 0,
            events: vec![
                ("Event1", "INFO"),
                ("Event2", "INFO"),
                ("Event3", "CRITICAL"),
                ("Event4", "ERROR"),
                ("Event5", "INFO"),
                ("Event6", "INFO"),
                ("Event7", "WARNING"),
                ("Event8", "INFO"),
                ("Event9", "INFO"),
                ("Event10", "INFO"),
                ("Event11", "CRITICAL"),
                ("Event12", "INFO"),
                ("Event13", "INFO"),
                ("Event14", "INFO"),
                ("Event15", "INFO"),
                ("Event16", "INFO"),
                ("Event17", "ERROR"),
                ("Event18", "ERROR"),
                ("Event19", "INFO"),
                ("Event20", "INFO"),
                ("Event21", "WARNING"),
                ("Event22", "INFO"),
                ("Event23", "INFO"),
                ("Event24", "WARNING"),
                ("Event25", "INFO"),
                ("Event26", "INFO"),
            ],
            info_style: Style::default().fg(Color::White),
            warning_style: Style::default().fg(Color::Yellow),
            error_style: Style::default().fg(Color::Magenta),
            critical_style: Style::default().fg(Color::Red),
        }
    }

    fn advance(&mut self) {
        let event = self.events.pop().unwrap();
        self.events.insert(0, event);
    }
}

enum Event {
    Input(event::Key),
    Tick,
}

fn main() {
    // Terminal initialization
    let backend = MouseBackend::new().unwrap();
    let mut terminal = Terminal::new(backend).unwrap();

    // Channels
    let (tx, rx) = mpsc::channel();
    let input_tx = tx.clone();
    let clock_tx = tx.clone();

    // Input
    thread::spawn(move || {
        let stdin = io::stdin();
        for c in stdin.keys() {
            let evt = c.unwrap();
            input_tx.send(Event::Input(evt)).unwrap();
            if evt == event::Key::Char('q') {
                break;
            }
        }
    });

    // Tick
    thread::spawn(move || loop {
        clock_tx.send(Event::Tick).unwrap();
        thread::sleep(time::Duration::from_millis(500));
    });

    // App
    let mut app = App::new();

    // First draw call
    terminal.clear().unwrap();
    terminal.hide_cursor().unwrap();
    app.size = terminal.size().unwrap();
    draw(&mut terminal, &app);

    loop {
        let size = terminal.size().unwrap();
        if size != app.size {
            terminal.resize(size).unwrap();
            app.size = size;
        }

        let evt = rx.recv().unwrap();
        match evt {
            Event::Input(input) => match input {
                event::Key::Char('q') => {
                    break;
                }
                event::Key::Down => {
                    app.selected += 1;
                    if app.selected > app.items.len() - 1 {
                        app.selected = 0;
                    }
                }
                event::Key::Up => if app.selected > 0 {
                    app.selected -= 1;
                } else {
                    app.selected = app.items.len() - 1;
                },
                _ => {}
            },
            Event::Tick => {
                app.advance();
            }
        }
        draw(&mut terminal, &app);
    }

    terminal.show_cursor().unwrap();
}

fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
    Group::default()
        .direction(Direction::Horizontal)
        .sizes(&[Size::Percent(50), Size::Percent(50)])
        .render(t, &app.size, |t, chunks| {
            SelectableList::default()
                .block(Block::default().borders(Borders::ALL).title("List"))
                .items(&app.items)
                .select(app.selected)
                .highlight_style(Style::default().fg(Color::Yellow).modifier(Modifier::Bold))
                .highlight_symbol(">")
                .render(t, &chunks[0]);
            {
                let events = app.events.iter().map(|&(evt, level)| {
                    Item::StyledData(
                        format!("{}: {}", level, evt),
                        match level {
                            "ERROR" => &app.error_style,
                            "CRITICAL" => &app.critical_style,
                            "WARNING" => &app.warning_style,
                            _ => &app.info_style,
                        },
                    )
                });
                List::new(events)
                    .block(Block::default().borders(Borders::ALL).title("List"))
                    .render(t, &chunks[1]);
            }
        });

    t.draw().unwrap();
}
<commit_msg>[examples][list] Change style of first list<commit_after>extern crate termion;
extern crate tui;

use std::io;
use std::thread;
use std::time;
use std::sync::mpsc;

use termion::event;
use termion::input::TermRead;

use tui::Terminal;
use tui::backend::MouseBackend;
use tui::widgets::{Block, Borders, Item, List, SelectableList, Widget};
use tui::layout::{Direction, Group, Rect, Size};
use tui::style::{Color, Modifier, Style};

struct App<'a> {
    size: Rect,
    items: Vec<&'a str>,
    selected: usize,
    events: Vec<(&'a str, &'a str)>,
    info_style: Style,
    warning_style: Style,
    error_style: Style,
    critical_style: Style,
}

impl<'a> App<'a> {
    fn new() -> App<'a> {
        App {
            size: Rect::default(),
            items: vec![
                "Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7", "Item8", "Item9",
                "Item10", "Item11", "Item12", "Item13", "Item14", "Item15", "Item16", "Item17",
                "Item18", "Item19", "Item20", "Item21", "Item22", "Item23", "Item24",
            ],
            selected: 0,
            events: vec![
                ("Event1", "INFO"),
                ("Event2", "INFO"),
                ("Event3", "CRITICAL"),
                ("Event4", "ERROR"),
                ("Event5", "INFO"),
                ("Event6", "INFO"),
                ("Event7", "WARNING"),
                ("Event8", "INFO"),
                ("Event9", "INFO"),
                ("Event10", "INFO"),
                ("Event11", "CRITICAL"),
                ("Event12", "INFO"),
                ("Event13", "INFO"),
                ("Event14", "INFO"),
                ("Event15", "INFO"),
                ("Event16", "INFO"),
                ("Event17", "ERROR"),
                ("Event18", "ERROR"),
                ("Event19", "INFO"),
                ("Event20", "INFO"),
                ("Event21", "WARNING"),
                ("Event22", "INFO"),
                ("Event23", "INFO"),
                ("Event24", "WARNING"),
                ("Event25", "INFO"),
                ("Event26", "INFO"),
            ],
            info_style: Style::default().fg(Color::White),
            warning_style: Style::default().fg(Color::Yellow),
            error_style: Style::default().fg(Color::Magenta),
            critical_style: Style::default().fg(Color::Red),
        }
    }

    fn advance(&mut self) {
        let event = self.events.pop().unwrap();
        self.events.insert(0, event);
    }
}

enum Event {
    Input(event::Key),
    Tick,
}

fn main() {
    // Terminal initialization
    let backend = MouseBackend::new().unwrap();
    let mut terminal = Terminal::new(backend).unwrap();

    // Channels
    let (tx, rx) = mpsc::channel();
    let input_tx = tx.clone();
    let clock_tx = tx.clone();

    // Input
    thread::spawn(move || {
        let stdin = io::stdin();
        for c in stdin.keys() {
            let evt = c.unwrap();
            input_tx.send(Event::Input(evt)).unwrap();
            if evt == event::Key::Char('q') {
                break;
            }
        }
    });

    // Tick
    thread::spawn(move || loop {
        clock_tx.send(Event::Tick).unwrap();
        thread::sleep(time::Duration::from_millis(500));
    });

    // App
    let mut app = App::new();

    // First draw call
    terminal.clear().unwrap();
    terminal.hide_cursor().unwrap();
    app.size = terminal.size().unwrap();
    draw(&mut terminal, &app);

    loop {
        let size = terminal.size().unwrap();
        if size != app.size {
            terminal.resize(size).unwrap();
            app.size = size;
        }

        let evt = rx.recv().unwrap();
        match evt {
            Event::Input(input) => match input {
                event::Key::Char('q') => {
                    break;
                }
                event::Key::Down => {
                    app.selected += 1;
                    if app.selected > app.items.len() - 1 {
                        app.selected = 0;
                    }
                }
                event::Key::Up => if app.selected > 0 {
                    app.selected -= 1;
                } else {
                    app.selected = app.items.len() - 1;
                },
                _ => {}
            },
            Event::Tick => {
                app.advance();
            }
        }
        draw(&mut terminal, &app);
    }

    terminal.show_cursor().unwrap();
}

fn draw(t: &mut Terminal<MouseBackend>, app: &App) {
    Group::default()
        .direction(Direction::Horizontal)
        .sizes(&[Size::Percent(50), Size::Percent(50)])
        .render(t, &app.size, |t, chunks| {
            let style = Style::default().fg(Color::Black).bg(Color::White);
            SelectableList::default()
                .block(Block::default().borders(Borders::ALL).title("List"))
                .items(&app.items)
                .select(app.selected)
                .style(style)
                .highlight_style(style.clone().fg(Color::LightGreen).modifier(Modifier::Bold))
                .highlight_symbol(">")
                .render(t, &chunks[0]);
            {
                let events = app.events.iter().map(|&(evt, level)| {
                    Item::StyledData(
                        format!("{}: {}", level, evt),
                        match level {
                            "ERROR" => &app.error_style,
                            "CRITICAL" => &app.critical_style,
                            "WARNING" => &app.warning_style,
                            _ => &app.info_style,
                        },
                    )
                });
                List::new(events)
                    .block(Block::default().borders(Borders::ALL).title("List"))
                    .render(t, &chunks[1]);
            }
        });

    t.draw().unwrap();
}
<|endoftext|>


<commit_before>use super::*;

// This is the runtime cursor on iteration
// It hold a copy of the containing page next page lock guard
// These lock guards are preventing the node and their neighbourhoods been changed externally
// Ordering are specified that can also change lock pattern
pub struct RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub index: usize,
    pub ordering: Ordering,
    pub page: Option<NodeCellRef>,
    pub deleted: DeletionSet,
    pub marker: PhantomData<(KS, PS)>,
    pub current: Option<EntryKey>,
}

impl<KS, PS> RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub fn new(pos: usize, page: &NodeCellRef, ordering: Ordering, deleted: &DeletionSet) -> Self {
        let mut cursor = RTCursor {
            index: pos,
            ordering,
            page: Some(page.clone()),
            deleted: deleted.clone(),
            marker: PhantomData,
            current: None,
        };
        match ordering {
            Ordering::Forward
                if pos >= read_node(page, |node: &NodeReadHandler<KS, PS>| node.len()) =>
            {
                cursor.next();
            }
            _ => {
                cursor.current = Some(Self::read_current(page, pos));
            }
        }
        debug!(
            "Created cursor with pos {}, current {:?}, ordering: {:?}",
            cursor.index, cursor.current, cursor.ordering
        );
        cursor
    }
    fn boxed(self) -> Box<IndexCursor> {
        box self
    }

    fn read_current(node: &NodeCellRef, pos: usize) -> EntryKey {
        read_node(node, |node: &NodeReadHandler<KS, PS>| {
            node.extnode().keys.as_slice_immute()[pos].clone()
        })
    }

    fn next_candidate(&mut self) -> bool {
        loop {
            let search_result = if self.page.is_some() {
                let current_page = self.page.clone().unwrap();
                read_node(&current_page, |page: &NodeReadHandler<KS, PS>| {
                    // let ext_page = page.extnode();
                    // debug!("Next id with index: {}, length: {}", self.index + 1, ext_page.len);
                    match self.ordering {
                        Ordering::Forward => {
                            if page.is_empty() || self.index + 1 >= page.len() {
                                let next_node_ref = page.right_ref().unwrap();
                                return read_node(
                                    next_node_ref,
                                    |next_node: &NodeReadHandler<KS, PS>| {
                                        if next_node.is_none() {
                                            self.page = None;
                                            self.current = None;
                                            return Some(false);
                                        } else if next_node.is_empty() {
                                            return None;
                                        } else if next_node.is_ext() {
                                            self.index = 0;
                                            self.page = Some(next_node_ref.clone());
                                            self.current =
                                                Some(Self::read_current(next_node_ref, self.index));
                                            return Some(true);
                                        } else {
                                            unreachable!()
                                        }
                                    },
                                );
                            } else {
                                self.index += 1;
                                // debug!("Advancing cursor to index {}", self.index);
                            }
                        }
                        Ordering::Backward => {
                            if page.is_empty() || self.index == 0 {
                                let prev_node_ref = page.left_ref().unwrap();
                                return read_node(
                                    prev_node_ref,
                                    |prev_node: &NodeReadHandler<KS, PS>| {
                                        if prev_node.is_none() {
                                            self.page = None;
                                            self.current = None;
                                            return Some(false);
                                        } else if prev_node.is_empty() {
                                            return None;
                                        } else if prev_node.is_ext() {
                                            self.index = prev_node.len() - 1;
                                            self.page = Some(prev_node_ref.clone());
                                            self.current =
                                                Some(Self::read_current(prev_node_ref, self.index));
                                            return Some(true);
                                        } else {
                                            unreachable!()
                                        }
                                    },
                                );
                            } else {
                                self.index -= 1;
                                // debug!("Advancing cursor to index {}", self.index);
                            }
                        }
                    }
                    self.current = Some(Self::read_current(&current_page, self.index));
                    Some(true)
                })

            } else {
                Some(false)
            };

            if let Some(res) = search_result {
                return res;
            }
        }
    }
}

impl<KS, PS> IndexCursor for RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    // TODO: Copy current after next
    fn next(&mut self) -> bool {
        loop {
            let has_candidate = self.next_candidate();
            if has_candidate {
                // search in deleted set and skip if exists
                if !self.deleted.read().contains(self.current().unwrap()) {
                    return true;
                }
            } else {
                return false;
            }
        }
    }

    // TODO: Use copied key reference
    fn current(&self) -> Option<&EntryKey> {
        self.current.as_ref()
    }
}
<commit_msg>cope with cursor read failure<commit_after>use super::*;

// This is the runtime cursor on iteration
// It hold a copy of the containing page next page lock guard
// These lock guards are preventing the node and their neighbourhoods been changed externally
// Ordering are specified that can also change lock pattern
pub struct RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub index: usize,
    pub ordering: Ordering,
    pub page: Option<NodeCellRef>,
    pub deleted: DeletionSet,
    pub marker: PhantomData<(KS, PS)>,
    pub current: Option<EntryKey>,
}

impl<KS, PS> RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    pub fn new(pos: usize, page: &NodeCellRef, ordering: Ordering, deleted: &DeletionSet) -> Self {
        let mut cursor = RTCursor {
            index: pos,
            ordering,
            page: Some(page.clone()),
            deleted: deleted.clone(),
            marker: PhantomData,
            current: None,
        };
        match ordering {
            Ordering::Forward
                if pos >= read_node(page, |node: &NodeReadHandler<KS, PS>| node.len()) =>
            {
                cursor.next();
            }
            _ => {
                cursor.current = Self::read_current(page, pos);
            }
        }
        debug!(
            "Created cursor with pos {}, current {:?}, ordering: {:?}",
            cursor.index, cursor.current, cursor.ordering
        );
        cursor
    }
    fn boxed(self) -> Box<IndexCursor> {
        box self
    }

    fn read_current(node: &NodeCellRef, pos: usize) -> Option<EntryKey> {
        read_node(node, |node: &NodeReadHandler<KS, PS>| {
            // node can be empty only if the node have been changed in the middle
            // if so the node should be reloaded from the outside by `read_node` function
            if node.is_empty_node() {
                None
            } else {
                Some(node.extnode().keys.as_slice_immute()[pos].clone())
            }
        })
    }

    fn next_candidate(&mut self) -> bool {
        loop {
            let search_result = if self.page.is_some() {
                let current_page = self.page.clone().unwrap();
                read_node(&current_page, |page: &NodeReadHandler<KS, PS>| {
                    // let ext_page = page.extnode();
                    // debug!("Next id with index: {}, length: {}", self.index + 1, ext_page.len);
                    match self.ordering {
                        Ordering::Forward => {
                            if page.is_empty() || self.index + 1 >= page.len() {
                                let next_node_ref = page.right_ref().unwrap();
                                return read_node(
                                    next_node_ref,
                                    |next_node: &NodeReadHandler<KS, PS>| {
                                        if next_node.is_none() {
                                            self.page = None;
                                            self.current = None;
                                            return Some(false);
                                        } else if next_node.is_empty() {
                                            return None;
                                        } else if next_node.is_ext() {
                                            self.index = 0;
                                            self.page = Some(next_node_ref.clone());
                                            self.current = Self::read_current(next_node_ref, self.index);
                                            return Some(true);
                                        } else {
                                            unreachable!()
                                        }
                                    },
                                );
                            } else {
                                self.index += 1;
                                // debug!("Advancing cursor to index {}", self.index);
                            }
                        }
                        Ordering::Backward => {
                            if page.is_empty() || self.index == 0 {
                                let prev_node_ref = page.left_ref().unwrap();
                                return read_node(
                                    prev_node_ref,
                                    |prev_node: &NodeReadHandler<KS, PS>| {
                                        if prev_node.is_none() {
                                            self.page = None;
                                            self.current = None;
                                            return Some(false);
                                        } else if prev_node.is_empty() {
                                            return None;
                                        } else if prev_node.is_ext() {
                                            self.index = prev_node.len() - 1;
                                            self.page = Some(prev_node_ref.clone());
                                            self.current = Self::read_current(prev_node_ref, self.index);
                                            return Some(true);
                                        } else {
                                            unreachable!()
                                        }
                                    },
                                );
                            } else {
                                self.index -= 1;
                                // debug!("Advancing cursor to index {}", self.index);
                            }
                        }
                    }
                    self.current = Self::read_current(&current_page, self.index);
                    Some(true)
                })

            } else {
                Some(false)
            };

            if let Some(res) = search_result {
                return res;
            }
        }
    }
}

impl<KS, PS> IndexCursor for RTCursor<KS, PS>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    // TODO: Copy current after next
    fn next(&mut self) -> bool {
        loop {
            let has_candidate = self.next_candidate();
            if has_candidate {
                // search in deleted set and skip if exists
                if !self.deleted.read().contains(self.current().unwrap()) {
                    return true;
                }
            } else {
                return false;
            }
        }
    }

    // TODO: Use copied key reference
    fn current(&self) -> Option<&EntryKey> {
        self.current.as_ref()
    }
}
<|endoftext|>


<commit_before>use std::thread::{JoinHandle, spawn};
use std::sync::mpsc::{Receiver, channel};
use std::io::{self, BufRead};
extern crate itertools;
use self::itertools::Itertools;
use std::sync::mpsc::TryRecvError;

extern crate either;

pub enum Decision {
    None,
    ImportLines(String),
    ShowCategories,
    ResetCursor,
    Respond,
    Tell(String),
    ConnectServer,
    ConnectIrc(String),
    ConnectDiscord(String),
}

pub fn new() -> Iter {
    let (sender, receiver) = channel();
    Iter{
        _thread: spawn(move || {
            let stdin = io::stdin();
            for line in stdin.lock().lines() {
                match line {
                    Ok(s) => {
                        if sender.send(s).is_err() {
                            break;
                        }
                    },
                    Err(_) => {
                        break;
                    }
                }
            }
        }),
        receiver: receiver,
    }
}

pub struct Iter {
    _thread: JoinHandle<()>,
    receiver: Receiver<String>,
}

impl Iterator for Iter {
    type Item = Decision;

    fn next(&mut self) -> Option<Decision> {
        match self.receiver.try_recv() {
            Ok(s) => {
                let params = s.split('`')
                    .enumerate()
                    .flat_map(|(iter, s)| {
                        // If we are not in a quotation
                        if iter % 2 == 0 {
                            either::Left(s.split(' '))
                        } else {
                            use std::iter::once;
                            either::Right(once(s))
                        }
                    })
                    .filter(|s| !s.is_empty()).collect_vec();

                match params.len() {
                    0 => {
                        println!("Available commands: import, connect, list, respond, tell");
                        Some(Decision::ResetCursor)
                    },
                    _ => {
                        match params[0] {
                            "import" => {
                                if params.len() != 3 {
                                    println!("Ignored: import takes 2 params");
                                    Some(Decision::ResetCursor)
                                } else {
                                    match params[1] {
                                        "lines" => {
                                            println!("Importing lines from `{}`...", params[2]);
                                            Some(Decision::ImportLines(params[2].to_string()))
                                        },
                                        _ => {
                                            println!("Ignored: Unrecognized import type");
                                            Some(Decision::ResetCursor)
                                        },
                                    }
                                }
                            },
                            "connect" => {
                                if params.len() < 2 {
                                    println!("Ignored: connect takes at least a connect type");
                                    println!("Connect types: server, irc, discord");
                                    Some(Decision::ResetCursor)
                                } else {
                                    match params[1] {
                                        "server" => {
                                            if params.len() != 2 {
                                                println!("Ignored: no extra parameters needed");
                                                Some(Decision::ResetCursor)
                                            } else {
                                                Some(Decision::ConnectServer)
                                            }
                                        },
                                        "irc" => {
                                            if params.len() != 3 {
                                                println!("Ignored: connect irc requires a config path");
                                                Some(Decision::ResetCursor)
                                            } else {
                                                Some(Decision::ConnectIrc(params[3].to_string()))
                                            }
                                        },
                                        "discord" => {
                                            if params.len() != 3 {
                                                println!("Ignored: connect discord requires a config path");
                                                Some(Decision::ResetCursor)
                                            } else {
                                                Some(Decision::ConnectDiscord(params[3].to_string()))
                                            }
                                        },
                                        _ => {
                                            println!("Ignored: Unrecognized connect type");
                                            Some(Decision::ResetCursor)
                                        },
                                    }
                                }
                            },
                            "list" => {
                                if params.len() != 2 {
                                    println!("Ignored: list takes 1 param");
                                    Some(Decision::ResetCursor)
                                } else {
                                    match params[1] {
                                        "categories" => {
                                            Some(Decision::ShowCategories)
                                        },
                                        _ => {
                                            println!("Ignored: Unrecognized list type");
                                            Some(Decision::ResetCursor)
                                        },
                                    }
                                }
                            },
                            "respond" => {
                                if params.len() != 1 {
                                    println!("Ignored: respond takes no params");
                                    Some(Decision::ResetCursor)
                                } else {
                                    Some(Decision::Respond)
                                }
                            },
                            "tell" => {
                                if params.len() != 2 {
                                    println!("Ignored: respond takes 1 param");
                                    Some(Decision::ResetCursor)
                                } else {
                                    Some(Decision::Tell(params[1].to_string()))
                                }
                            },
                            _ => {
                                println!("Ignored: Unrecognized command");
                                Some(Decision::ResetCursor)
                            },
                        }
                    }
                }
            },
            Err(TryRecvError::Empty) => {
                Some(Decision::None)
            },
            Err(TryRecvError::Disconnected) => {
                None
            },
        }
    }
}
<commit_msg>improve help and fix irc/discord commands<commit_after>use std::thread::{JoinHandle, spawn};
use std::sync::mpsc::{Receiver, channel};
use std::io::{self, BufRead};
extern crate itertools;
use self::itertools::Itertools;
use std::sync::mpsc::TryRecvError;

extern crate either;

pub enum Decision {
    None,
    ImportLines(String),
    ShowCategories,
    ResetCursor,
    Respond,
    Tell(String),
    ConnectServer,
    ConnectIrc(String),
    ConnectDiscord(String),
}

pub fn new() -> Iter {
    let (sender, receiver) = channel();
    Iter{
        _thread: spawn(move || {
            let stdin = io::stdin();
            for line in stdin.lock().lines() {
                match line {
                    Ok(s) => {
                        if sender.send(s).is_err() {
                            break;
                        }
                    },
                    Err(_) => {
                        break;
                    }
                }
            }
        }),
        receiver: receiver,
    }
}

pub struct Iter {
    _thread: JoinHandle<()>,
    receiver: Receiver<String>,
}

impl Iterator for Iter {
    type Item = Decision;

    fn next(&mut self) -> Option<Decision> {
        match self.receiver.try_recv() {
            Ok(s) => {
                let params = s.split('`')
                    .enumerate()
                    .flat_map(|(iter, s)| {
                        // If we are not in a quotation
                        if iter % 2 == 0 {
                            either::Left(s.split(' '))
                        } else {
                            use std::iter::once;
                            either::Right(once(s))
                        }
                    })
                    .filter(|s| !s.is_empty()).collect_vec();

                let help = || {
                    println!("Available commands: import, connect, list, respond, tell");
                    Some(Decision::ResetCursor)
                };

                match params.len() {
                    0 => {
                        help()
                    },
                    _ => {
                        match params[0] {
                            "help" => {
                                help()
                            },
                            "import" => {
                                if params.len() < 2 {
                                    println!("Usage: import <import type>");
                                    println!("Available import types: lines");
                                    Some(Decision::ResetCursor)
                                } else {
                                    match params[1] {
                                        "lines" => {
                                            if params.len() != 3 {
                                                println!("Usage: import lines <filname>");
                                                Some(Decision::ResetCursor)
                                            } else {
                                                println!("Importing lines from `{}`...", params[2]);
                                                Some(Decision::ImportLines(params[2].to_string()))
                                            }
                                        },
                                        _ => {
                                            println!("Ignored: Unrecognized import type");
                                            Some(Decision::ResetCursor)
                                        },
                                    }
                                }
                            },
                            "connect" => {
                                if params.len() < 2 {
                                    println!("Ignored: connect takes at least a connect type");
                                    println!("Connect types: server, irc, discord");
                                    Some(Decision::ResetCursor)
                                } else {
                                    match params[1] {
                                        "server" => {
                                            if params.len() != 2 {
                                                println!("Ignored: no extra parameters needed");
                                                Some(Decision::ResetCursor)
                                            } else {
                                                Some(Decision::ConnectServer)
                                            }
                                        },
                                        "irc" => {
                                            if params.len() != 3 {
                                                println!("Ignored: connect irc requires a config path");
                                                Some(Decision::ResetCursor)
                                            } else {
                                                Some(Decision::ConnectIrc(params[2].to_string()))
                                            }
                                        },
                                        "discord" => {
                                            if params.len() != 3 {
                                                println!("Ignored: connect discord requires a config path");
                                                Some(Decision::ResetCursor)
                                            } else {
                                                Some(Decision::ConnectDiscord(params[2].to_string()))
                                            }
                                        },
                                        _ => {
                                            println!("Ignored: Unrecognized connect type");
                                            Some(Decision::ResetCursor)
                                        },
                                    }
                                }
                            },
                            "list" => {
                                if params.len() != 2 {
                                    println!("Ignored: list takes 1 param");
                                    Some(Decision::ResetCursor)
                                } else {
                                    match params[1] {
                                        "categories" => {
                                            Some(Decision::ShowCategories)
                                        },
                                        _ => {
                                            println!("Ignored: Unrecognized list type");
                                            Some(Decision::ResetCursor)
                                        },
                                    }
                                }
                            },
                            "respond" => {
                                if params.len() != 1 {
                                    println!("Ignored: respond takes no params");
                                    Some(Decision::ResetCursor)
                                } else {
                                    Some(Decision::Respond)
                                }
                            },
                            "tell" => {
                                if params.len() != 2 {
                                    println!("Ignored: respond takes 1 param");
                                    Some(Decision::ResetCursor)
                                } else {
                                    Some(Decision::Tell(params[1].to_string()))
                                }
                            },
                            _ => {
                                println!("Ignored: Unrecognized command");
                                Some(Decision::ResetCursor)
                            },
                        }
                    }
                }
            },
            Err(TryRecvError::Empty) => {
                Some(Decision::None)
            },
            Err(TryRecvError::Disconnected) => {
                None
            },
        }
    }
}
<|endoftext|>