<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//

use cpu::interrupts::pics;
use cpu::interrupts::idt::Idt;

use cpu::context::InterruptFrame;
use cpu::dtable::DTable;

//==--------------------------------------------------------------------------==
// Top-level interrupt handling

/// Initialize interrupt handling.
///
/// This function initializes the PICs, populates the IDT with interrupt
/// handlers, loads the IDT pointer, and enables interrupts.
///
/// This is called from the kernel during the init process.
pub unsafe fn initialize() {

    pics::initialize();
   // TODO: consider loading double-fault handler before anything else in case
   //       a double fault occurs during init?
    IDT.load();         // Load the IDT pointer

    debug!("Testing interrupt handling");
    asm!("int $0" :: "N" (0xff));

    Idt::enable_interrupts(); // enable interrupts

}

macro_rules! exception_inner {
    ($title:expr, $kind:expr, $source:expr, $f:expr) => {
        use vga::{CONSOLE, Color};
        use core::fmt::Write;
        let _ = write!( CONSOLE.lock()
                               .set_colors(Color::White, Color::Blue)
                      , "EVERYTHING IS FINE: {}{} at {:p}\n\
                         Source: {}.\nThis is fine.\n\n\
                         {:?}"
                         , $title, $kind
                         , (*$f).rip
                         , $source
                         , *$f);
    };
    ($title:expr, $kind:expr, $source:expr, $f:expr, $e:expr) => {
        use vga::{CONSOLE, Color};
        use core::fmt::Write;
        let _ = write!( CONSOLE.lock()
                               .set_colors(Color::White, Color::Blue)
                      , "EVERYTHING IS FINE: {}{} at {:p}\n\
                         Source: {}.\n
                         Error code: {:x}\nThis is fine.\n\n\
                         {:?}"
                         , $title, $kind
                         , (*$f).rip
                         , $source
                         , $e
                         , *$f);
    };
}

macro_rules! exceptions {
    ( idt: $idt:expr, $($tail:tt)+ ) => {
        exceptions! {  @impl $idt, $($tail)+  }
    };
    ( @impl $i:expr, fault: $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => {
        #[doc=$title]
        extern "x86-interrupt" fn $name(frame: &InterruptFrame) {
            exception_inner! ($title, "Fault", $source, frame);
            loop {}
        }
        $i.$name.set_handler($name as InterruptHandler);
        exceptions! {  @impl  $i, $($tail)* }
    };
     ( @impl $i:expr, fault (code): $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => {
        #[doc=$title]
        extern "x86-interrupt" fn $name( frame: &InterruptFrame
                                       , error_code: usize) {
           exception_inner! ($title, "Fault", $source, frame, error_code);
           loop {}
       }
       $i.$name.set_handler($name as ErrorCodeHandler);
       exceptions! { @impl $i, $($tail)* };
   };
     ( @impl $i:expr, trap: $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => {
         #[doc=$title]
         extern "x86-interrupt" fn $name(frame: &InterruptFrame) {
             exception_inner! ($title, "Trap", $source, frame);
         }
         $i.$name.set_handler($name as InterruptHandler)
           .set_trap();
         exceptions! { @impl $i, $($tail)* };
     };
      ( @impl $i:expr, ) => {};

}

lazy_static! {
    static ref IDT: Idt = {
        let mut idt = Idt::new();
        use cpu::interrupts::*;

        // TODO: log each handler as it's added to the IDT? that way we can
        //       trace faults occurring during IDT population (if any)
        //          - eliza, 5/22/2017
        exceptions! { idt: idt,
            fault: divide_by_zero, "Divide by Zero Error",
                   "DIV or IDIV instruction",
            fault: nmi, "Non-Maskable Interrupt",
                  "Non-maskable external interrupt",
            trap: overflow, "Overflow", "INTO instruction",
            fault: bound_exceeded, "BOUND range exceeded",
                  "BOUND instruction",
            fault: undefined_opcode, "Undefined Opcode",
                   "UD2 instruction or reserved opcode",
            fault: device_not_available, "Device Not Available"
                 , "Floating-point or WAIT/FWAIT instruction \
                    (no math coprocessor)",
            fault (code): double_fault, "Double Fault"
                 , "Any instruction that can generate an exception, a NMI, or \
                    an INTR",
            fault (code): invalid_tss, "Invalid TSS"
                 , "Task switch or TSS access",
            fault (code): segment_not_present, "Segment Not Present"
                 , "Loading segment registers or accessing \
                    system segments",
            fault (code): general_protection_fault, "General Protection Fault"
                 , "Any memory reference or other protection checks",
            fault (code): stack_segment_fault, "Stack Segment Fault"
                 , "Stack operations and SS register loads",
            fault: floating_point_error
                 , "x87 FPU Floating-Point Error (Math Fault)"
                 , "x87 FPU floating-point or WAIT/FWAIT instruction",
            fault: machine_check, "Machine Check"
                 , "Model-dependent (probably hardware!)",
            fault (code): alignment_check, "Alignment Check"
                 , "Any data reference in memory",
            fault: simd_fp_exception, "SIMD Floating-Point Exception"
                 , "SSE/SSE2/SSE3 floating-point instructions",
        };

        idt.breakpoint.set_handler(breakpoint as InterruptHandler);
        idt.page_fault.set_handler(page_fault as ErrorCodeHandler);
        idt[0x20].set_handler(timer as InterruptHandler);
        idt[0x21].set_handler(keyboard as InterruptHandler);
        idt[0xff].set_handler(test as InterruptHandler);

        kinfoln!( dots: " . . ", target: "Adding interrupt handlers to IDT"
                , "[ OKAY ]");
        idt
    };
}

#[no_mangle] #[inline(never)]
pub extern "x86-interrupt" fn keyboard(_frame: &InterruptFrame) {
    use io::keyboard;

    // println!("keyboard happened");
    if let Some(input) = keyboard::read_char() {
        if input == '\r' {
            println!("");
        } else {
            print!("{}", input);
        }
    }
   // send the PICs the end interrupt signal
   unsafe {
       pics::end_pic_interrupt(0x21);
   }
}

#[no_mangle] #[inline(never)]
pub extern "x86-interrupt" fn breakpoint(frame: &InterruptFrame) {
    println!("Breakpoint! Frame: {:#?}", frame);
   // send the PICs the end interrupt signal
   unsafe {
       pics::end_pic_interrupt(0x03);
   }
}
<commit_msg>fix(interrupts): disable test interrupt again<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//

use cpu::interrupts::pics;
use cpu::interrupts::idt::Idt;

use cpu::context::InterruptFrame;
use cpu::dtable::DTable;

//==--------------------------------------------------------------------------==
// Top-level interrupt handling

/// Initialize interrupt handling.
///
/// This function initializes the PICs, populates the IDT with interrupt
/// handlers, loads the IDT pointer, and enables interrupts.
///
/// This is called from the kernel during the init process.
pub unsafe fn initialize() {

    pics::initialize();
   // TODO: consider loading double-fault handler before anything else in case
   //       a double fault occurs during init?
    IDT.load();         // Load the IDT pointer
    //
    // debug!("Testing interrupt handling");
    // asm!("int $0" :: "N" (0xff));

    Idt::enable_interrupts(); // enable interrupts

}

macro_rules! exception_inner {
    ($title:expr, $kind:expr, $source:expr, $f:expr) => {
        use vga::{CONSOLE, Color};
        use core::fmt::Write;
        let _ = write!( CONSOLE.lock()
                               .set_colors(Color::White, Color::Blue)
                      , "EVERYTHING IS FINE: {}{} at {:p}\n\
                         Source: {}.\nThis is fine.\n\n\
                         {:?}"
                         , $title, $kind
                         , (*$f).rip
                         , $source
                         , *$f);
    };
    ($title:expr, $kind:expr, $source:expr, $f:expr, $e:expr) => {
        use vga::{CONSOLE, Color};
        use core::fmt::Write;
        let _ = write!( CONSOLE.lock()
                               .set_colors(Color::White, Color::Blue)
                      , "EVERYTHING IS FINE: {}{} at {:p}\n\
                         Source: {}.\n
                         Error code: {:x}\nThis is fine.\n\n\
                         {:?}"
                         , $title, $kind
                         , (*$f).rip
                         , $source
                         , $e
                         , *$f);
    };
}

macro_rules! exceptions {
    ( idt: $idt:expr, $($tail:tt)+ ) => {
        exceptions! {  @impl $idt, $($tail)+  }
    };
    ( @impl $i:expr, fault: $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => {
        #[doc=$title]
        extern "x86-interrupt" fn $name(frame: &InterruptFrame) {
            exception_inner! ($title, "Fault", $source, frame);
            loop {}
        }
        $i.$name.set_handler($name as InterruptHandler);
        exceptions! {  @impl  $i, $($tail)* }
    };
     ( @impl $i:expr, fault (code): $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => {
        #[doc=$title]
        extern "x86-interrupt" fn $name( frame: &InterruptFrame
                                       , error_code: usize) {
           exception_inner! ($title, "Fault", $source, frame, error_code);
           loop {}
       }
       $i.$name.set_handler($name as ErrorCodeHandler);
       exceptions! { @impl $i, $($tail)* };
   };
     ( @impl $i:expr, trap: $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => {
         #[doc=$title]
         extern "x86-interrupt" fn $name(frame: &InterruptFrame) {
             exception_inner! ($title, "Trap", $source, frame);
         }
         $i.$name.set_handler($name as InterruptHandler)
           .set_trap();
         exceptions! { @impl $i, $($tail)* };
     };
      ( @impl $i:expr, ) => {};

}

lazy_static! {
    static ref IDT: Idt = {
        let mut idt = Idt::new();
        use cpu::interrupts::*;

        // TODO: log each handler as it's added to the IDT? that way we can
        //       trace faults occurring during IDT population (if any)
        //          - eliza, 5/22/2017
        exceptions! { idt: idt,
            fault: divide_by_zero, "Divide by Zero Error",
                   "DIV or IDIV instruction",
            fault: nmi, "Non-Maskable Interrupt",
                  "Non-maskable external interrupt",
            trap: overflow, "Overflow", "INTO instruction",
            fault: bound_exceeded, "BOUND range exceeded",
                  "BOUND instruction",
            fault: undefined_opcode, "Undefined Opcode",
                   "UD2 instruction or reserved opcode",
            fault: device_not_available, "Device Not Available"
                 , "Floating-point or WAIT/FWAIT instruction \
                    (no math coprocessor)",
            fault (code): double_fault, "Double Fault"
                 , "Any instruction that can generate an exception, a NMI, or \
                    an INTR",
            fault (code): invalid_tss, "Invalid TSS"
                 , "Task switch or TSS access",
            fault (code): segment_not_present, "Segment Not Present"
                 , "Loading segment registers or accessing \
                    system segments",
            fault (code): general_protection_fault, "General Protection Fault"
                 , "Any memory reference or other protection checks",
            fault (code): stack_segment_fault, "Stack Segment Fault"
                 , "Stack operations and SS register loads",
            fault: floating_point_error
                 , "x87 FPU Floating-Point Error (Math Fault)"
                 , "x87 FPU floating-point or WAIT/FWAIT instruction",
            fault: machine_check, "Machine Check"
                 , "Model-dependent (probably hardware!)",
            fault (code): alignment_check, "Alignment Check"
                 , "Any data reference in memory",
            fault: simd_fp_exception, "SIMD Floating-Point Exception"
                 , "SSE/SSE2/SSE3 floating-point instructions",
        };

        idt.breakpoint.set_handler(breakpoint as InterruptHandler);
        idt.page_fault.set_handler(page_fault as ErrorCodeHandler);
        idt[0x20].set_handler(timer as InterruptHandler);
        idt[0x21].set_handler(keyboard as InterruptHandler);
        idt[0xff].set_handler(test as InterruptHandler);

        kinfoln!( dots: " . . ", target: "Adding interrupt handlers to IDT"
                , "[ OKAY ]");
        idt
    };
}

#[no_mangle] #[inline(never)]
pub extern "x86-interrupt" fn keyboard(_frame: &InterruptFrame) {
    use io::keyboard;

    // println!("keyboard happened");
    if let Some(input) = keyboard::read_char() {
        if input == '\r' {
            println!("");
        } else {
            print!("{}", input);
        }
    }
   // send the PICs the end interrupt signal
   unsafe {
       pics::end_pic_interrupt(0x21);
   }
}

#[no_mangle] #[inline(never)]
pub extern "x86-interrupt" fn breakpoint(frame: &InterruptFrame) {
    println!("Breakpoint! Frame: {:#?}", frame);
   // send the PICs the end interrupt signal
   unsafe {
       pics::end_pic_interrupt(0x03);
   }
}
<|endoftext|>


<commit_before>use std::{cmp::Ordering, path::Path};

use async_trait::async_trait;
use indexmap::IndexMap;
use regex::Regex;
use serde::{Deserialize, Serialize};
use serde_json;

use crate::{
    build::BuildStep,
    bundle::{
        layout::{ios::IOsKbdLayer, IOsPlatform},
        KbdgenBundle,
    },
    util::split_keys,
};

#[derive(Serialize, Deserialize)]
pub struct IosInfo {
    name: String,
    locale: String,
    enter: String,
    space: String,
}

/// Removes all occurances of `character` in `input`
pub fn remove_all_occurances(input: String, character: char) -> String {
    input
        .as_str()
        .chars()
        .filter(|x| x.cmp(&character) != Ordering::Equal)
        .into_iter()
        .collect::<String>()
}

pub fn keyboard_component_from_string(input: String) -> Option<IosButton> {
    let regex = Regex::new(r"^\\s\{([^}:]+)(?::(\d+(?:\.\d+)?))?\}$").expect("valid regex");
    let captures = regex.captures(input.as_str());

    if let Some(captures) = captures {
        if let Some(id) = captures.get(1) {
            let id = match id.as_str().chars().next().unwrap().cmp(&'\"') {
                Ordering::Equal => remove_all_occurances(id.as_str().to_string(), '\"'),
                _ => format!("_{}", remove_all_occurances(id.as_str().to_string(), '\"')),
            };
            if let Some(width) = captures.get(2) {
                Some(IosButton {
                    id: id,
                    width: width.as_str().parse::<f32>().expect("invalid float value"),
                })
            } else {
                Some(IosButton {
                    id: id,
                    width: 1.0f32,
                })
            }
        } else {
            return None;
        }
    } else {
        return None;
    }
}

#[derive(Serialize, Deserialize)]
pub struct IosButton {
    id: String,
    width: f32,
}

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum IosKeyMapType {
    Character(String),
    Button(IosButton),
}

#[derive(Serialize, Deserialize)]
pub struct IosLayer {
    #[serde(flatten)]
    layer: IndexMap<String, Vec<Vec<IosKeyMapType>>>,
}

#[derive(Serialize, Deserialize)]
pub struct IosDeadKeys {
    iphone: IndexMap<String, String>,
    #[serde(rename = "ipad-9in")]
    i_pad_9in: IndexMap<String, String>,
    #[serde(rename = "ipad-12in")]
    i_pad_12in: IndexMap<String, String>,
}

#[derive(Serialize, Deserialize)]
pub struct IosKeyboardDefinitions {
    #[serde(flatten)]
    info: IosInfo,
    longpress: IndexMap<String, Vec<String>>,
    dead_keys: IosDeadKeys,
    transforms: serde_json::value::Value,
    iphone: IosLayer,
    i_pad_9in: IosLayer,
    i_pad_12in: IosLayer,
}

pub fn ios_layer_name(layer: &IOsKbdLayer) -> String {
    match layer {
        IOsKbdLayer::Default => "normal",
        IOsKbdLayer::Shift => "shifted",
        IOsKbdLayer::Alt => "alt",
        IOsKbdLayer::AltAndShift => "alt+shift",
        IOsKbdLayer::Symbols1 => "symbols-1",
        IOsKbdLayer::Symbols2 => "symbols-2",
    }
    .to_string()
}

pub fn generate_platform(platform: &IOsPlatform) -> IndexMap<String, Vec<Vec<IosKeyMapType>>> {
    let mut layers: IndexMap<String, Vec<Vec<IosKeyMapType>>> = IndexMap::new();
    for (layer_name, layer_key_map) in &platform.layers {
        let layer_name = ios_layer_name(layer_name);
        let key_map_rows: Vec<&str> = layer_key_map
            .trim()
            .split("\n")
            .map(|x| x.clone())
            .collect();
        let mut layer_rows: Vec<Vec<IosKeyMapType>> = Vec::new();
        for key_map in key_map_rows {
            let key_map = split_keys(key_map);
            let mut new_key_map: Vec<IosKeyMapType> = Vec::new();
            for key in key_map {
                if let Some(keyboard_entity) = keyboard_component_from_string(key.clone()) {
                    new_key_map.push(IosKeyMapType::Button(keyboard_entity));
                } else {
                    new_key_map.push(IosKeyMapType::Character(key));
                }
            }
            layer_rows.push(new_key_map)
        }
        layers.insert(layer_name, layer_rows);
    }
    layers
}

pub struct GenerateIos;

#[async_trait(?Send)]
impl BuildStep for GenerateIos {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let keyboard_definitions_file_path = output_path.join("KeyboardDefinitions.json");

        for (language_tag, layout) in &bundle.layouts {
            let mut longpress: IndexMap<String, Vec<String>> = IndexMap::new();
            let mut iphone_layers: IndexMap<String, Vec<Vec<IosKeyMapType>>> = IndexMap::new();
            let mut i_pad_9in_layers: IndexMap<String, Vec<Vec<IosKeyMapType>>> = IndexMap::new();
            let mut i_pad_12in_layers: IndexMap<String, Vec<Vec<IosKeyMapType>>> = IndexMap::new();

            if let Some(ios_target) = &layout.i_os {
                if let Some(layout_longpress) = &layout.longpress {
                    for (key, value) in layout_longpress {
                        longpress.insert(key.clone(), value.clone());
                    }
                }

                if let Some(primary_platform) = &ios_target.primary {
                    iphone_layers.extend(generate_platform(&primary_platform));
                }
                if let Some(i_pad_9in_platform) = &ios_target.i_pad_9in {
                    i_pad_9in_layers.extend(generate_platform(&i_pad_9in_platform));
                }
                if let Some(i_pad_12in_platform) = &ios_target.i_pad_12in {
                    i_pad_12in_layers.extend(generate_platform(&i_pad_12in_platform));
                }

                if let Some(key_names) = &layout.key_names {
                    std::fs::write(
                        output_path.join(keyboard_definitions_file_path.clone()),
                        serde_json::to_string_pretty(&[IosKeyboardDefinitions {
                            info: IosInfo {
                                name: layout
                                    .display_names
                                    .get(language_tag)
                                    .expect("can't evaluate language tag of layout")
                                    .to_string(),
                                locale: language_tag.to_string(),
                                enter: key_names.r#return.to_string(),
                                space: key_names.space.to_string(),
                            },
                            longpress: longpress,
                            dead_keys: IosDeadKeys {
                                iphone: IndexMap::new(),
                                i_pad_9in: IndexMap::new(),
                                i_pad_12in: IndexMap::new(),
                            },
                            transforms: serde_json::value::Value::Null,
                            iphone: IosLayer {
                                layer: iphone_layers,
                            },
                            i_pad_9in: IosLayer {
                                layer: i_pad_9in_layers,
                            },
                            i_pad_12in: IosLayer {
                                layer: i_pad_12in_layers,
                            },
                        }])
                        .unwrap(),
                    )
                    .unwrap();
                }
            }
        }
    }
}
<commit_msg>rename layer -> layout in place where wrong<commit_after>use std::{cmp::Ordering, path::Path};

use async_trait::async_trait;
use indexmap::IndexMap;
use regex::Regex;
use serde::{Deserialize, Serialize};
use serde_json;

use crate::{
    build::BuildStep,
    bundle::{
        layout::{ios::IOsKbdLayer, IOsPlatform},
        KbdgenBundle,
    },
    util::split_keys,
};

#[derive(Serialize, Deserialize)]
pub struct IosInfo {
    name: String,
    locale: String,
    enter: String,
    space: String,
}

/// Removes all occurances of `character` in `input`
pub fn remove_all_occurances(input: String, character: char) -> String {
    input
        .as_str()
        .chars()
        .filter(|x| x.cmp(&character) != Ordering::Equal)
        .into_iter()
        .collect::<String>()
}

pub fn keyboard_component_from_string(input: String) -> Option<IosButton> {
    let regex = Regex::new(r"^\\s\{([^}:]+)(?::(\d+(?:\.\d+)?))?\}$").expect("valid regex");
    let captures = regex.captures(input.as_str());

    if let Some(captures) = captures {
        if let Some(id) = captures.get(1) {
            let id = match id.as_str().chars().next().unwrap().cmp(&'\"') {
                Ordering::Equal => remove_all_occurances(id.as_str().to_string(), '\"'),
                _ => format!("_{}", remove_all_occurances(id.as_str().to_string(), '\"')),
            };
            if let Some(width) = captures.get(2) {
                Some(IosButton {
                    id: id,
                    width: width.as_str().parse::<f32>().expect("invalid float value"),
                })
            } else {
                Some(IosButton {
                    id: id,
                    width: 1.0f32,
                })
            }
        } else {
            return None;
        }
    } else {
        return None;
    }
}

#[derive(Serialize, Deserialize)]
pub struct IosButton {
    id: String,
    width: f32,
}

#[derive(Serialize, Deserialize)]
#[serde(untagged)]
pub enum IosKeyMapType {
    Character(String),
    Button(IosButton),
}

#[derive(Serialize, Deserialize)]
pub struct IosLayout {
    #[serde(flatten)]
    layer: IndexMap<String, Vec<Vec<IosKeyMapType>>>,
}

#[derive(Serialize, Deserialize)]
pub struct IosDeadKeys {
    iphone: IndexMap<String, String>,
    #[serde(rename = "ipad-9in")]
    i_pad_9in: IndexMap<String, String>,
    #[serde(rename = "ipad-12in")]
    i_pad_12in: IndexMap<String, String>,
}

#[derive(Serialize, Deserialize)]
pub struct IosKeyboardDefinitions {
    #[serde(flatten)]
    info: IosInfo,
    longpress: IndexMap<String, Vec<String>>,
    dead_keys: IosDeadKeys,
    transforms: serde_json::value::Value,
    iphone: IosLayout,
    i_pad_9in: IosLayout,
    i_pad_12in: IosLayout,
}

pub fn ios_layer_name(layer: &IOsKbdLayer) -> String {
    match layer {
        IOsKbdLayer::Default => "normal",
        IOsKbdLayer::Shift => "shifted",
        IOsKbdLayer::Alt => "alt",
        IOsKbdLayer::AltAndShift => "alt+shift",
        IOsKbdLayer::Symbols1 => "symbols-1",
        IOsKbdLayer::Symbols2 => "symbols-2",
    }
    .to_string()
}

pub fn generate_platform(platform: &IOsPlatform) -> IndexMap<String, Vec<Vec<IosKeyMapType>>> {
    let mut layers: IndexMap<String, Vec<Vec<IosKeyMapType>>> = IndexMap::new();
    for (layer_name, layer_key_map) in &platform.layers {
        let layer_name = ios_layer_name(layer_name);
        let key_map_rows: Vec<&str> = layer_key_map
            .trim()
            .split("\n")
            .map(|x| x.clone())
            .collect();
        let mut layer_rows: Vec<Vec<IosKeyMapType>> = Vec::new();
        for key_map in key_map_rows {
            let key_map = split_keys(key_map);
            let mut new_key_map: Vec<IosKeyMapType> = Vec::new();
            for key in key_map {
                if let Some(keyboard_entity) = keyboard_component_from_string(key.clone()) {
                    new_key_map.push(IosKeyMapType::Button(keyboard_entity));
                } else {
                    new_key_map.push(IosKeyMapType::Character(key));
                }
            }
            layer_rows.push(new_key_map)
        }
        layers.insert(layer_name, layer_rows);
    }
    layers
}

pub struct GenerateIos;

#[async_trait(?Send)]
impl BuildStep for GenerateIos {
    async fn build(&self, bundle: &KbdgenBundle, output_path: &Path) {
        let keyboard_definitions_file_path = output_path.join("KeyboardDefinitions.json");

        for (language_tag, layout) in &bundle.layouts {
            let mut longpress: IndexMap<String, Vec<String>> = IndexMap::new();
            let mut iphone_layers: IndexMap<String, Vec<Vec<IosKeyMapType>>> = IndexMap::new();
            let mut i_pad_9in_layers: IndexMap<String, Vec<Vec<IosKeyMapType>>> = IndexMap::new();
            let mut i_pad_12in_layers: IndexMap<String, Vec<Vec<IosKeyMapType>>> = IndexMap::new();

            if let Some(ios_target) = &layout.i_os {
                if let Some(layout_longpress) = &layout.longpress {
                    for (key, value) in layout_longpress {
                        longpress.insert(key.clone(), value.clone());
                    }
                }

                if let Some(primary_platform) = &ios_target.primary {
                    iphone_layers.extend(generate_platform(&primary_platform));
                }
                if let Some(i_pad_9in_platform) = &ios_target.i_pad_9in {
                    i_pad_9in_layers.extend(generate_platform(&i_pad_9in_platform));
                }
                if let Some(i_pad_12in_platform) = &ios_target.i_pad_12in {
                    i_pad_12in_layers.extend(generate_platform(&i_pad_12in_platform));
                }

                if let Some(key_names) = &layout.key_names {
                    std::fs::write(
                        output_path.join(keyboard_definitions_file_path.clone()),
                        serde_json::to_string_pretty(&[IosKeyboardDefinitions {
                            info: IosInfo {
                                name: layout
                                    .display_names
                                    .get(language_tag)
                                    .expect("can't evaluate language tag of layout")
                                    .to_string(),
                                locale: language_tag.to_string(),
                                enter: key_names.r#return.to_string(),
                                space: key_names.space.to_string(),
                            },
                            longpress: longpress,
                            dead_keys: IosDeadKeys {
                                iphone: IndexMap::new(),
                                i_pad_9in: IndexMap::new(),
                                i_pad_12in: IndexMap::new(),
                            },
                            transforms: serde_json::value::Value::Null,
                            iphone: IosLayout {
                                layer: iphone_layers,
                            },
                            i_pad_9in: IosLayout {
                                layer: i_pad_9in_layers,
                            },
                            i_pad_12in: IosLayout {
                                layer: i_pad_12in_layers,
                            },
                        }])
                        .unwrap(),
                    )
                    .unwrap();
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>import driver::session;

import option::{none, some};

import std::map::hashmap;
import vec;

import syntax::ast::{crate, expr_, expr_mac, mac_invoc};
import syntax::fold::*;
import syntax::ext::base::*;
import syntax::parse::parser::parse_expr_from_source_str;

fn expand_expr(exts: hashmap<str, syntax_extension>, cx: ext_ctxt, e: expr_,
               fld: ast_fold, orig: fn@(expr_, ast_fold) -> expr_) -> expr_ {
    ret alt e {
          expr_mac(mac) {
            alt mac.node {
              mac_invoc(pth, args, body) {
                assert (vec::len(pth.node.idents) > 0u);
                let extname = pth.node.idents[0];
                alt exts.find(extname) {
                  none. {
                    cx.span_fatal(pth.span,
                                  #fmt["macro undefined: '%s'", extname])
                  }
                  some(normal(ext)) {
                    let expanded = ext(cx, pth.span, args, body);

                    cx.bt_push(mac.span);
                    //keep going, outside-in
                    let fully_expanded = fld.fold_expr(expanded).node;
                    cx.bt_pop();

                    fully_expanded
                  }
                  some(macro_defining(ext)) {
                    let named_extension = ext(cx, pth.span, args, body);
                    exts.insert(named_extension.ident, named_extension.ext);
                    ast::expr_rec([], none)
                  }
                }
              }
              _ { cx.span_bug(mac.span, "naked syntactic bit") }
            }
          }
          _ { orig(e, fld) }
        };
}

// FIXME: this is a terrible kludge to inject some macros into the default
// compilation environment. When the macro-definition system is substantially
// more mature, these should move from here, into a compiled part of libcore
// at very least.

fn core_macros() -> str {
    ret
"{
    #macro[[#error[f, ...], log_full(core::error, #fmt[f, ...])]];
    #macro[[#warn[f, ...], log_full(core::warn, #fmt[f, ...])]];
    #macro[[#info[f, ...], log_full(core::info, #fmt[f, ...])]];
    #macro[[#debug[f, ...], log_full(core::debug, #fmt[f, ...])]];
}";
}

fn expand_crate(sess: session::session, c: @crate) -> @crate {
    let exts = syntax_expander_table();
    let afp = default_ast_fold();
    let cx: ext_ctxt = mk_ctxt(sess);
    let f_pre =
        {fold_expr: bind expand_expr(exts, cx, _, _, afp.fold_expr)
            with *afp};
    let f = make_fold(f_pre);
    let cm = parse_expr_from_source_str("-", core_macros(),
                                        sess.get_opts().cfg,
                                        sess.get_parse_sess());

    // This is run for its side-effects on the expander env,
    // as it registers all the core macros as expanders.
    f.fold_expr(cm);

    let res = @f.fold_crate(*c);
    ret res;
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>Switch core macros to expand to log(level, ...).<commit_after>import driver::session;

import option::{none, some};

import std::map::hashmap;
import vec;

import syntax::ast::{crate, expr_, expr_mac, mac_invoc};
import syntax::fold::*;
import syntax::ext::base::*;
import syntax::parse::parser::parse_expr_from_source_str;

fn expand_expr(exts: hashmap<str, syntax_extension>, cx: ext_ctxt, e: expr_,
               fld: ast_fold, orig: fn@(expr_, ast_fold) -> expr_) -> expr_ {
    ret alt e {
          expr_mac(mac) {
            alt mac.node {
              mac_invoc(pth, args, body) {
                assert (vec::len(pth.node.idents) > 0u);
                let extname = pth.node.idents[0];
                alt exts.find(extname) {
                  none. {
                    cx.span_fatal(pth.span,
                                  #fmt["macro undefined: '%s'", extname])
                  }
                  some(normal(ext)) {
                    let expanded = ext(cx, pth.span, args, body);

                    cx.bt_push(mac.span);
                    //keep going, outside-in
                    let fully_expanded = fld.fold_expr(expanded).node;
                    cx.bt_pop();

                    fully_expanded
                  }
                  some(macro_defining(ext)) {
                    let named_extension = ext(cx, pth.span, args, body);
                    exts.insert(named_extension.ident, named_extension.ext);
                    ast::expr_rec([], none)
                  }
                }
              }
              _ { cx.span_bug(mac.span, "naked syntactic bit") }
            }
          }
          _ { orig(e, fld) }
        };
}

// FIXME: this is a terrible kludge to inject some macros into the default
// compilation environment. When the macro-definition system is substantially
// more mature, these should move from here, into a compiled part of libcore
// at very least.

fn core_macros() -> str {
    ret
"{
    #macro[[#error[f, ...], log(core::error, #fmt[f, ...])]];
    #macro[[#warn[f, ...], log(core::warn, #fmt[f, ...])]];
    #macro[[#info[f, ...], log(core::info, #fmt[f, ...])]];
    #macro[[#debug[f, ...], log(core::debug, #fmt[f, ...])]];
}";
}

fn expand_crate(sess: session::session, c: @crate) -> @crate {
    let exts = syntax_expander_table();
    let afp = default_ast_fold();
    let cx: ext_ctxt = mk_ctxt(sess);
    let f_pre =
        {fold_expr: bind expand_expr(exts, cx, _, _, afp.fold_expr)
            with *afp};
    let f = make_fold(f_pre);
    let cm = parse_expr_from_source_str("-", core_macros(),
                                        sess.get_opts().cfg,
                                        sess.get_parse_sess());

    // This is run for its side-effects on the expander env,
    // as it registers all the core macros as expanders.
    f.fold_expr(cm);

    let res = @f.fold_crate(*c);
    ret res;
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before>/// Wrapper around Vec which supports fast indexing and rotation
///
/// The rotation implemented by grid::Storage is a simple integer addition.
/// Compare with standard library rotation which requires rearranging items in
/// memory.
///
/// As a consequence, the indexing operators need to be reimplemented for this
/// type to account for the 0th element not always being at the start of the
/// allocation.
///
/// Because certain Vec operations are no longer valid on this type, no Deref
/// implementation is provided. Anything from Vec that should be exposed must be
/// done so manually.
use std::ops::{Index, IndexMut};

use index::{IndexRange, Line};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Storage<T> {
    inner: Vec<T>,
    zero: usize,
    visible_lines: Line,
}

impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> {
    fn eq(&self, other: &Self) -> bool {
        let mut equal = true;
        for i in IndexRange(Line(0) .. self.visible_lines) {
            equal = equal && (self[i] == other[i])
        }
        equal
    }
}

impl<T> Storage<T> {
    #[inline]
    pub fn with_capacity(cap: usize, lines: Line) -> Storage<T> {
        Storage {
            inner: Vec::with_capacity(cap),
            zero: 0,
            visible_lines: lines - 1,
        }
    }

    #[inline]
    pub fn capacity(&self) -> usize {
        self.inner.capacity()
    }

    /// Increase the number of lines in the buffer
    pub fn grow_visible_lines(&mut self, next: Line, template_row: T)
    where
        T: Clone,
    {
        // Calculate insert position (before the first line)
        let offset = self.zero % self.inner.len();

        // Insert new template row for every line grown
        let lines_to_grow = (next - (self.visible_lines + 1)).0;
        for _ in 0..lines_to_grow {
            self.inner.insert(offset, template_row.clone());
        }

        // Set zero to old zero + lines grown
        self.zero = offset + lines_to_grow;

        // Update visible lines
        self.visible_lines = next - 1;
    }

    /// Decrease the number of lines in the buffer
    pub fn shrink_visible_lines(&mut self, next: Line) {
        // Calculate shrinkage and last line of buffer
        let shrinkage = (self.visible_lines + 1 - next).0;
        let offset = (self.zero + self.inner.len() - 1) % self.inner.len();

        // Generate range of lines that have to be deleted before the zero line
        let start = offset.saturating_sub(shrinkage - 1);
        let shrink_before = start..=offset;

        // Generate range of lines that have to be deleted after the zero line
        let shrink_after = (self.inner.len() + offset + 1 - shrinkage)..self.inner.len();

        // Delete all lines in reverse order
        for i in shrink_before.chain(shrink_after).rev() {
            self.inner.remove(i);
        }

        // Check if zero has moved (not the first line in the buffer)
        if self.zero % (self.inner.len() + shrinkage) != 0 {
            // Set zero to the first deleted line in the buffer
            self.zero = start;
        }

        // Update visible lines
        self.visible_lines = next - 1;
    }

    #[inline]
    pub fn push(&mut self, item: T) {
        self.inner.push(item)
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.inner.len()
    }

    /// Compute actual index in underlying storage given the requested index.
    #[inline]
    fn compute_index(&self, requested: usize) -> usize {
        (requested + self.zero) % self.len()
    }

    fn compute_line_index(&self, requested: Line) -> usize {
        ((self.len() + self.zero + *self.visible_lines) - *requested) % self.len()
    }

    pub fn swap_lines(&mut self, a: Line, b: Line) {
        let a = self.compute_line_index(a);
        let b = self.compute_line_index(b);
        self.inner.swap(a, b);
    }

    pub fn iter_mut(&mut self) -> IterMut<T> {
        IterMut { storage: self, index: 0 }
    }

    pub fn rotate(&mut self, count: isize) {
        let len = self.len();
        assert!(count.abs() as usize <= len);
        self.zero += (count + len as isize) as usize % len;
    }

    // Fast path
    pub fn rotate_up(&mut self, count: usize) {
        self.zero = (self.zero + count) % self.len();
    }
}

impl<T> Index<usize> for Storage<T> {
    type Output = T;
    #[inline]
    fn index(&self, index: usize) -> &T {
        let index = self.compute_index(index); // borrowck
        &self.inner[index]
    }
}

impl<T> IndexMut<usize> for Storage<T> {
    #[inline]
    fn index_mut(&mut self, index: usize) -> &mut T {
        let index = self.compute_index(index); // borrowck
        &mut self.inner[index]
    }
}

impl<T> Index<Line> for Storage<T> {
    type Output = T;
    #[inline]
    fn index(&self, index: Line) -> &T {
        let index = self.visible_lines - index;
        &self[*index]
    }
}

impl<T> IndexMut<Line> for Storage<T> {
    #[inline]
    fn index_mut(&mut self, index: Line) -> &mut T {
        let index = self.visible_lines - index;
        &mut self[*index]
    }
}

pub struct IterMut<'a, T: 'a> {
    storage: &'a mut Storage<T>,
    index: usize,
}

impl<'a, T: 'a> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        if self.index == self.storage.len() {
            None
        } else {
            let index = self.index;
            self.index += 1;

            unsafe {
                Some(&mut *(&mut self.storage[index] as *mut _))
            }
        }
    }
}

/// Grow the buffer one line at the end of the buffer
///
/// Before:
///   0: 0 <- Zero
///   1: 1
///   2: -
/// After:
///   0: -
///   1: 0 <- Zero
///   2: 1
///   3: -
#[test]
fn grow_after_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["0", "1", "-"],
        zero: 0,
        visible_lines: Line(2),
    };

    // Grow buffer
    storage.grow_visible_lines(Line(4), "-");

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["-", "0", "1", "-"],
        zero: 1,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}

/// Grow the buffer one line at the start of the buffer
///
/// Before:
///   0: -
///   1: 0 <- Zero
///   2: 1
/// After:
///   0: -
///   1: -
///   2: 0 <- Zero
///   3: 1
#[test]
fn grow_before_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["-", "0", "1"],
        zero: 1,
        visible_lines: Line(2),
    };

    // Grow buffer
    storage.grow_visible_lines(Line(4), "-");

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["-", "-", "0", "1"],
        zero: 2,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}

/// Shrink the buffer one line at the start of the buffer
///
/// Before:
///   0: 2
///   1: 0 <- Zero
///   2: 1
/// After:
///   0: 0 <- Zero
///   1: 1
#[test]
fn shrink_before_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["2", "0", "1"],
        zero: 1,
        visible_lines: Line(2),
    };

    // Shrink buffer
    storage.shrink_visible_lines(Line(2));

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["0", "1"],
        zero: 0,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}

/// Shrink the buffer one line at the end of the buffer
///
/// Before:
///   0: 0 <- Zero
///   1: 1
///   2: 2
/// After:
///   0: 0 <- Zero
///   1: 1
#[test]
fn shrink_after_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["0", "1", "2"],
        zero: 0,
        visible_lines: Line(2),
    };

    // Shrink buffer
    storage.shrink_visible_lines(Line(2));

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["0", "1"],
        zero: 0,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}

/// Shrink the buffer at the start and end of the buffer
///
/// Before:
///   0: 4
///   1: 5
///   2: 0 <- Zero
///   3: 1
///   4: 2
///   5: 3
/// After:
///   0: 0 <- Zero
///   1: 1
#[test]
fn shrink_before_and_after_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["4", "5", "0", "1", "2", "3"],
        zero: 2,
        visible_lines: Line(5),
    };

    // Shrink buffer
    storage.shrink_visible_lines(Line(2));

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["0", "1"],
        zero: 0,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}
<commit_msg>Compile on stable<commit_after>/// Wrapper around Vec which supports fast indexing and rotation
///
/// The rotation implemented by grid::Storage is a simple integer addition.
/// Compare with standard library rotation which requires rearranging items in
/// memory.
///
/// As a consequence, the indexing operators need to be reimplemented for this
/// type to account for the 0th element not always being at the start of the
/// allocation.
///
/// Because certain Vec operations are no longer valid on this type, no Deref
/// implementation is provided. Anything from Vec that should be exposed must be
/// done so manually.
use std::ops::{Index, IndexMut};

use index::{IndexRange, Line};

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Storage<T> {
    inner: Vec<T>,
    zero: usize,
    visible_lines: Line,
}

impl<T: PartialEq> ::std::cmp::PartialEq for Storage<T> {
    fn eq(&self, other: &Self) -> bool {
        let mut equal = true;
        for i in IndexRange(Line(0) .. self.visible_lines) {
            equal = equal && (self[i] == other[i])
        }
        equal
    }
}

impl<T> Storage<T> {
    #[inline]
    pub fn with_capacity(cap: usize, lines: Line) -> Storage<T> {
        Storage {
            inner: Vec::with_capacity(cap),
            zero: 0,
            visible_lines: lines - 1,
        }
    }

    #[inline]
    pub fn capacity(&self) -> usize {
        self.inner.capacity()
    }

    /// Increase the number of lines in the buffer
    pub fn grow_visible_lines(&mut self, next: Line, template_row: T)
    where
        T: Clone,
    {
        // Calculate insert position (before the first line)
        let offset = self.zero % self.inner.len();

        // Insert new template row for every line grown
        let lines_to_grow = (next - (self.visible_lines + 1)).0;
        for _ in 0..lines_to_grow {
            self.inner.insert(offset, template_row.clone());
        }

        // Set zero to old zero + lines grown
        self.zero = offset + lines_to_grow;

        // Update visible lines
        self.visible_lines = next - 1;
    }

    /// Decrease the number of lines in the buffer
    pub fn shrink_visible_lines(&mut self, next: Line) {
        // Calculate shrinkage and last line of buffer
        let shrinkage = (self.visible_lines + 1 - next).0;
        let offset = (self.zero + self.inner.len() - 1) % self.inner.len();

        // Generate range of lines that have to be deleted before the zero line
        let start = offset.saturating_sub(shrinkage - 1);
        let shrink_before = start..(offset + 1);

        // Generate range of lines that have to be deleted after the zero line
        let shrink_after = (self.inner.len() + offset + 1 - shrinkage)..self.inner.len();

        // Delete all lines in reverse order
        for i in shrink_before.chain(shrink_after).rev() {
            self.inner.remove(i);
        }

        // Check if zero has moved (not the first line in the buffer)
        if self.zero % (self.inner.len() + shrinkage) != 0 {
            // Set zero to the first deleted line in the buffer
            self.zero = start;
        }

        // Update visible lines
        self.visible_lines = next - 1;
    }

    #[inline]
    pub fn push(&mut self, item: T) {
        self.inner.push(item)
    }

    #[inline]
    pub fn len(&self) -> usize {
        self.inner.len()
    }

    /// Compute actual index in underlying storage given the requested index.
    #[inline]
    fn compute_index(&self, requested: usize) -> usize {
        (requested + self.zero) % self.len()
    }

    fn compute_line_index(&self, requested: Line) -> usize {
        ((self.len() + self.zero + *self.visible_lines) - *requested) % self.len()
    }

    pub fn swap_lines(&mut self, a: Line, b: Line) {
        let a = self.compute_line_index(a);
        let b = self.compute_line_index(b);
        self.inner.swap(a, b);
    }

    pub fn iter_mut(&mut self) -> IterMut<T> {
        IterMut { storage: self, index: 0 }
    }

    pub fn rotate(&mut self, count: isize) {
        let len = self.len();
        assert!(count.abs() as usize <= len);
        self.zero += (count + len as isize) as usize % len;
    }

    // Fast path
    pub fn rotate_up(&mut self, count: usize) {
        self.zero = (self.zero + count) % self.len();
    }
}

impl<T> Index<usize> for Storage<T> {
    type Output = T;
    #[inline]
    fn index(&self, index: usize) -> &T {
        let index = self.compute_index(index); // borrowck
        &self.inner[index]
    }
}

impl<T> IndexMut<usize> for Storage<T> {
    #[inline]
    fn index_mut(&mut self, index: usize) -> &mut T {
        let index = self.compute_index(index); // borrowck
        &mut self.inner[index]
    }
}

impl<T> Index<Line> for Storage<T> {
    type Output = T;
    #[inline]
    fn index(&self, index: Line) -> &T {
        let index = self.visible_lines - index;
        &self[*index]
    }
}

impl<T> IndexMut<Line> for Storage<T> {
    #[inline]
    fn index_mut(&mut self, index: Line) -> &mut T {
        let index = self.visible_lines - index;
        &mut self[*index]
    }
}

pub struct IterMut<'a, T: 'a> {
    storage: &'a mut Storage<T>,
    index: usize,
}

impl<'a, T: 'a> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;

    fn next(&mut self) -> Option<Self::Item> {
        if self.index == self.storage.len() {
            None
        } else {
            let index = self.index;
            self.index += 1;

            unsafe {
                Some(&mut *(&mut self.storage[index] as *mut _))
            }
        }
    }
}

/// Grow the buffer one line at the end of the buffer
///
/// Before:
///   0: 0 <- Zero
///   1: 1
///   2: -
/// After:
///   0: -
///   1: 0 <- Zero
///   2: 1
///   3: -
#[test]
fn grow_after_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["0", "1", "-"],
        zero: 0,
        visible_lines: Line(2),
    };

    // Grow buffer
    storage.grow_visible_lines(Line(4), "-");

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["-", "0", "1", "-"],
        zero: 1,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}

/// Grow the buffer one line at the start of the buffer
///
/// Before:
///   0: -
///   1: 0 <- Zero
///   2: 1
/// After:
///   0: -
///   1: -
///   2: 0 <- Zero
///   3: 1
#[test]
fn grow_before_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["-", "0", "1"],
        zero: 1,
        visible_lines: Line(2),
    };

    // Grow buffer
    storage.grow_visible_lines(Line(4), "-");

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["-", "-", "0", "1"],
        zero: 2,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}

/// Shrink the buffer one line at the start of the buffer
///
/// Before:
///   0: 2
///   1: 0 <- Zero
///   2: 1
/// After:
///   0: 0 <- Zero
///   1: 1
#[test]
fn shrink_before_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["2", "0", "1"],
        zero: 1,
        visible_lines: Line(2),
    };

    // Shrink buffer
    storage.shrink_visible_lines(Line(2));

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["0", "1"],
        zero: 0,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}

/// Shrink the buffer one line at the end of the buffer
///
/// Before:
///   0: 0 <- Zero
///   1: 1
///   2: 2
/// After:
///   0: 0 <- Zero
///   1: 1
#[test]
fn shrink_after_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["0", "1", "2"],
        zero: 0,
        visible_lines: Line(2),
    };

    // Shrink buffer
    storage.shrink_visible_lines(Line(2));

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["0", "1"],
        zero: 0,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}

/// Shrink the buffer at the start and end of the buffer
///
/// Before:
///   0: 4
///   1: 5
///   2: 0 <- Zero
///   3: 1
///   4: 2
///   5: 3
/// After:
///   0: 0 <- Zero
///   1: 1
#[test]
fn shrink_before_and_after_zero() {
    // Setup storage area
    let mut storage = Storage {
        inner: vec!["4", "5", "0", "1", "2", "3"],
        zero: 2,
        visible_lines: Line(5),
    };

    // Shrink buffer
    storage.shrink_visible_lines(Line(2));

    // Make sure the result is correct
    let expected = Storage {
        inner: vec!["0", "1"],
        zero: 0,
        visible_lines: Line(0),
    };
    assert_eq!(storage.inner, expected.inner);
    assert_eq!(storage.zero, expected.zero);
}
<|endoftext|>


<commit_before><commit_msg>allow refreshing cpu frequency<commit_after><|endoftext|>


<commit_before><commit_msg>Remove clone<commit_after><|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// force-host

#![crate_type="dylib"]
#![feature(plugin_registrar, quote, rustc_private)]

extern crate syntax;
extern crate syntax_pos;
extern crate rustc;
extern crate rustc_plugin;

use syntax::feature_gate::Features;
use syntax::parse::token::{NtExpr, NtPat};
use syntax::ast::{Ident, Pat};
use syntax::tokenstream::{TokenTree};
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::build::AstBuilder;
use syntax::ext::tt::quoted;
use syntax::ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal};
use syntax::ext::tt::macro_parser::{Success, Failure, Error};
use syntax::ext::tt::macro_parser::parse_failure_msg;
use syntax::ptr::P;
use syntax_pos::Span;
use rustc_plugin::Registry;

use std::cell::RefCell;

fn expand_mbe_matches(cx: &mut ExtCtxt, _: Span, args: &[TokenTree])
        -> Box<MacResult + 'static> {

    let mbe_matcher = quote_tokens!(cx, $$matched:expr, $$($$pat:pat)|+);
    let mbe_matcher = quoted::parse(mbe_matcher.into_iter().collect(),
                                    true,
                                    cx.parse_sess,
                                    &RefCell::new(Features::new()),
                                    &[]);
    let map = match TokenTree::parse(cx, &mbe_matcher, args.iter().cloned().collect()) {
        Success(map) => map,
        Failure(_, tok) => {
            panic!("expected Success, but got Failure: {}", parse_failure_msg(tok));
        }
        Error(_, s) => {
            panic!("expected Success, but got Error: {}", s);
        }
    };

    let matched_nt = match *map[&Ident::from_str("matched")] {
        MatchedNonterminal(ref nt) => nt.clone(),
        _ => unreachable!(),
    };

    let mac_expr = match (&*matched_nt, &*map[&Ident::from_str("pat")]) {
        (&NtExpr(ref matched_expr), &MatchedSeq(ref pats, seq_sp)) => {
            let pats: Vec<P<Pat>> = pats.iter().map(|pat_nt| {
                match *pat_nt {
                    MatchedNonterminal(ref nt) => match **nt {
                        NtPat(ref pat) => pat.clone(),
                        _ => unreachable!(),
                    },
                    _ => unreachable!(),
                }
            }).collect();
            let arm = cx.arm(seq_sp, pats, cx.expr_bool(seq_sp, true));

            quote_expr!(cx,
                match $matched_expr {
                    $arm
                    _ => false
                }
            )
        }
        _ => unreachable!()
    };

    MacEager::expr(mac_expr)
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("matches", expand_mbe_matches);
}
<commit_msg>Fix procedural_mbe_matching test case after libsyntax change.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// force-host

#![crate_type="dylib"]
#![feature(plugin_registrar, quote, rustc_private)]

extern crate syntax;
extern crate syntax_pos;
extern crate rustc;
extern crate rustc_plugin;

use syntax::feature_gate::Features;
use syntax::parse::token::{NtExpr, NtPat};
use syntax::ast::{Ident, Pat};
use syntax::tokenstream::{TokenTree};
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::build::AstBuilder;
use syntax::ext::tt::quoted;
use syntax::ext::tt::macro_parser::{MatchedSeq, MatchedNonterminal};
use syntax::ext::tt::macro_parser::{Success, Failure, Error};
use syntax::ext::tt::macro_parser::parse_failure_msg;
use syntax::ptr::P;
use syntax_pos::Span;
use rustc_plugin::Registry;

use std::cell::RefCell;

fn expand_mbe_matches(cx: &mut ExtCtxt, _: Span, args: &[TokenTree])
        -> Box<MacResult + 'static> {

    let mbe_matcher = quote_tokens!(cx, $$matched:expr, $$($$pat:pat)|+);
    let mbe_matcher = quoted::parse(mbe_matcher.into_iter().collect(),
                                    true,
                                    cx.parse_sess,
                                    &Features::new(),
                                    &[]);
    let map = match TokenTree::parse(cx, &mbe_matcher, args.iter().cloned().collect()) {
        Success(map) => map,
        Failure(_, tok) => {
            panic!("expected Success, but got Failure: {}", parse_failure_msg(tok));
        }
        Error(_, s) => {
            panic!("expected Success, but got Error: {}", s);
        }
    };

    let matched_nt = match *map[&Ident::from_str("matched")] {
        MatchedNonterminal(ref nt) => nt.clone(),
        _ => unreachable!(),
    };

    let mac_expr = match (&*matched_nt, &*map[&Ident::from_str("pat")]) {
        (&NtExpr(ref matched_expr), &MatchedSeq(ref pats, seq_sp)) => {
            let pats: Vec<P<Pat>> = pats.iter().map(|pat_nt| {
                match *pat_nt {
                    MatchedNonterminal(ref nt) => match **nt {
                        NtPat(ref pat) => pat.clone(),
                        _ => unreachable!(),
                    },
                    _ => unreachable!(),
                }
            }).collect();
            let arm = cx.arm(seq_sp, pats, cx.expr_bool(seq_sp, true));

            quote_expr!(cx,
                match $matched_expr {
                    $arm
                    _ => false
                }
            )
        }
        _ => unreachable!()
    };

    MacEager::expr(mac_expr)
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("matches", expand_mbe_matches);
}
<|endoftext|>


<commit_before>

use at::{AtRust, AtTrigger};
use plugins::get;

use select::document::Document;
use select::predicate::{Attr, Class, Name};
use rand;

pub fn eksi(trigger: &AtTrigger, at: &AtRust) {
    let url = format!("https://eksisozluk.com/{}", trigger.command_message);
    let body = get(&url[..]);

    let body_str = body.unwrap();
    let document = Document::from_str(&body_str[..]);

    let mut entries: Vec<String> = Vec::new();

    for entry in document.find(Attr("id", "entry-list")).find(Name("li")).iter() {
        let entry_text = entry.find(Class("content")).first().unwrap().text();
        // FIXME: Do I really need to clone text?
        entries.push(entry_text.clone());
    }

    at.reply(trigger, &entries[rand::random::<usize>() % entries.len()][..]);
}
<commit_msg>Make it much more idiomatic<commit_after>

use at::{AtRust, AtTrigger};
use plugins::get;

use select::document::Document;
use select::predicate::{Attr, Class, Name};
use rand;

pub fn get_eksi(query: &str) -> Option<Vec<String>> {

    let url = format!("https://eksisozluk.com/{}", query);

    let body = match get(&url[..]) {
        Some(n) => n,
        None => return None
    };

    let document = Document::from_str(&body[..]);

    let mut entries: Vec<String> = Vec::new();

    for entry in document.find(Attr("id", "entry-list")).find(Name("li")).iter() {
        let entry_text = entry.find(Class("content")).first().unwrap().text();
        // FIXME: Do I really need to clone text?
        entries.push(entry_text.clone());
    }

    return match entries.len() {
        0 => None,
        _ => Some(entries)
    };

}

pub fn eksi(trigger: &AtTrigger, at: &AtRust) {

    let entries = get_eksi(&trigger.command_message[..]);

    match entries {
        Some(entries) => at.reply(trigger, &entries[rand::random::<usize>() % entries.len()][..]),
        None => {}
    }

}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(clippy)]

extern crate irc as irclib;
extern crate evalbotlib as backend;
extern crate rustc_serialize;

use std::thread;
use std::time::Duration;
use std::sync::{Arc, mpsc};
use std::sync::atomic::{AtomicBool, Ordering};

use backend::{EvalSvc, EvalSvcCfg, Response, util};

use self::irclib::client::server::NetIrcServer;
use self::irclib::client::prelude as ircp;
use self::irclib::client::prelude::{Server, ServerExt};

macro_rules! send_msg {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_privmsg($dest, $line) {
            println!("failed to send message: {}", x)
        }
    };
}

type EvalWorkerSender = mpsc::Sender<(IrcMessage, SendMsgFnBox)>;
type EvalWorkerReceiver = mpsc::Receiver<(IrcMessage, SendMsgFnBox)>;
type SendMsgFnBox = Box<Fn(&str, &str) + Send>;

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct IrcCfg {
    output: OutputCfg,
    command_prefix: String,
    owners: Vec<String>,
    irc_networks: Vec<ircp::Config>
}

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct OutputCfg {
    chan_output_prefix: String,
    max_channel_lines: usize,
    max_channel_line_len: usize,
    max_priv_lines: usize
}

fn main() {
    let config = match util::decode::<IrcCfg>("evalbot.irc.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.irc.toml: {:?}", x);
            return;
        }
    };
    let quit_handle = Arc::new(AtomicBool::new(false));
    let (tx, rx) = mpsc::channel();

    {
        let svc = match start_evalsvc() {
            Ok(x) => x,
            Err(()) => return,
        };
        let ocfg = config.output.clone();
        thread::spawn(move || eval_worker(svc, rx, ocfg));
    }

    for (cfg, jh) in config.irc_networks
                           .iter()
                           .filter_map(|cfg| {
                               match ircp::IrcServer::from_config(cfg.clone()) {
                                   Ok(conn) => Some((cfg, conn)),
                                   Err(ref err) => {
                                       println!("failed to create IRC connection from Config {:#?}: {:?}", cfg, err);
                                       None
                                   }
                               }
                           })
                           .enumerate()
                           .map(|(k, (cfg, nwk))| {
                               (cfg,
                                start_worker(nwk,
                                             tx.clone(),
                                             config.owners.clone(),
                                             config.command_prefix.clone(),
                                             quit_handle.clone(),
                                             format!("{}", k)))
                           })
                           .collect::<Vec<_>>() {
        util::ignore(jh.join());
        println!("disconnected as {:?} from {:?}", cfg.nickname, cfg.server);
    }
}

fn start_evalsvc() -> Result<EvalSvc, ()> {
    let evalcfg = match util::decode::<EvalSvcCfg>("evalbot.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.toml: {:?}", x);
            return Err(());
        }
    };
    Ok(EvalSvc::new(evalcfg))
}

fn start_worker(conn: NetIrcServer,
    svc: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: Arc<AtomicBool>,
    conn_hash: String)
                -> thread::JoinHandle<()> {
    thread::spawn(move || {
        worker(conn, svc, owners, cmd_prefix, quit_handle, conn_hash);
    })
}

#[derive(Clone, Debug)]
struct MessageSource {
    chan: Option<String>,
    sender: String,
    conn_hash: String
}

impl MessageSource {
    fn reply_to(&self) -> &str {
        if let Some(ref chan) = self.chan { chan } else { &self.sender }
    }

    fn is_chan(&self) -> bool {
        self.chan.is_some()
    }
}

#[derive(Debug)]
struct IrcMessage {
    sender: MessageSource,
    data: MessageData
}

#[derive(Debug)]
enum MessageData {
    EvalReq {
        lang: String,
        code: String,
        timeout: bool
    },
    Quit,
    Rehash,
    Restart {
        lang: String
    }
}

unsafe impl Sync for IrcMessage {}

fn parse_msg(conn_hash: &str, message: &ircp::Message, owners: &[String], cmd_prefix: &str) -> Option<IrcMessage> {
    let sender = message.source_nickname().unwrap_or("").to_owned();
    if let ircp::Command::PRIVMSG(ref target, ref message) = message.command {
        let target = target.to_owned();
        let is_owner = owners.iter().any(|x| *x == sender);
        let sender = {
            let in_channel = target.starts_with('#');
            if !in_channel && message.contains('\x01') {
                // ignore CTCP
                return None;
            }

            MessageSource {
                chan: if in_channel { Some(target) } else { None },
                sender: sender,
                conn_hash: conn_hash.to_owned()
            }
        };

        if is_owner {
            if let Some(x) = try_cmd(&message, cmd_prefix) {
                return Some(IrcMessage { sender: sender, data: x });
            }
        }

        let tok: Vec<&str> = message.trim().splitn(2, '>').collect();
        if tok.len() == 2 {
            return Some(IrcMessage {
                sender: sender,
                data: MessageData::EvalReq { timeout: true, lang: tok[0].to_owned(), code: tok[1].to_owned() }
            });
        } else if !is_owner {
            return None;
        }

        let tok: Vec<&str> = message.trim().splitn(2, '#').collect();
        if tok.len() == 2 {
            Some(IrcMessage {
                sender: sender,
                data: MessageData::EvalReq { timeout: false, lang: tok[0].to_owned(), code: tok[1].to_owned() }
            })
        } else {
            None
        }
    } else {
        None
    }
}

fn try_cmd(msg: &str, cmd_prefix: &str) -> Option<MessageData> {
    if let Some((cmd, args)) = parse_cmd(msg, cmd_prefix) {
        match &cmd as &str {
            "quit" => Some(MessageData::Quit),
            "rehash" => Some(MessageData::Rehash),
            "restart" if !args.is_empty() => Some(MessageData::Restart { lang: args[0].to_owned() }),
            _ => None,
        }
    } else {
        None
    }
}

fn parse_cmd(msg: &str, cmd_prefix: &str) -> Option<(String, Vec<String>)> {
    let tok: Vec<&str> = match msg.splitn(2, cmd_prefix).nth(1).map(|x| x.split(' ').collect()) {
        Some(x) => x,
        None => return None,
    };

    if tok.is_empty() {
        return None;
    }

    Some((tok[0].to_owned(),
          tok.into_iter().skip(1).map(|x| x.to_owned()).collect::<Vec<String>>()))
}

fn worker(conn: NetIrcServer,
    tx: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: Arc<AtomicBool>,
    conn_hash: String) {
    'connection: loop {
        println!("connecting to to {:?} as {:?}", conn.config().server, conn.config().nickname);
        while let Err(x) = conn.identify() {
            println!("error while identify()ing; retrying: {:?}", x);
            thread::sleep(Duration::new(1, 0));
        }
        println!("connected to {:?} as {:?}", conn.config().server, conn.config().nickname);
        for msg in conn.iter() {
            if quit_handle.load(Ordering::Relaxed) {
                break 'connection;
            }
            let msg = match msg {
                Ok(x) => x,
                Err(_) => continue,
            };

            if let Some(msg) = parse_msg(&conn_hash, &msg, &owners, &cmd_prefix) {
                let conn = conn.clone();
                println!("M: {:?}", msg);
                match msg.data {
                    MessageData::EvalReq { .. } | MessageData::Rehash | MessageData::Restart { .. } => {
                        util::ignore(tx.send((msg, Box::new(move |to, r| send_msg!(conn, to, r)))))
                    }
                    MessageData::Quit => {
                        quit_handle.store(true, Ordering::Relaxed);
                        break 'connection;
                    }
                }
            }
        }
    }
    util::ignore(conn.send_quit("Quitting"));
}

fn eval_worker(mut svc: EvalSvc, rx: EvalWorkerReceiver, ocfg: OutputCfg) {
    while let Ok((msg, callback)) = rx.recv() {
        macro_rules! reply {
            ($x:expr) => { callback(msg.sender.reply_to(), $x); }
        }
        match msg.data {
            MessageData::EvalReq { lang, code, timeout } => {
                let ocfg = ocfg.clone();
                let sender = msg.sender.clone();
                svc.eval(&lang,
                         code,
                         timeout,
                         Some(format!("{}{}", msg.sender.conn_hash, msg.sender.reply_to())),
                         Box::new(move |resp| respond(resp, ocfg, sender, callback)))
            }
            MessageData::Rehash => {
                if let Ok(nsvc) = start_evalsvc() {
                    reply!("rehashed");
                    svc = nsvc;
                } else {
                    reply!("read config failed; check logs");
                }
            }
            MessageData::Restart { lang } => {
                match svc.restart(&lang) {
                    Ok(_) => reply!(&format!("restarted {}", lang)),
                    Err(_) => reply!(&format!("no such language {}", lang)),
                }
            }
            _ => println!("invalid thing sent to eval_worker?"),
        };
    }
}

fn sanitise_output(input: &str, prefix: Option<&str>, max_len: usize, max_lines: usize) -> Vec<String> {
    let (mut ret, initial_lines): (Vec<String>, usize) = {
        let med = input.lines()
                       .map(|l| l.trim())
                       .filter(|l| !l.is_empty())
                       .flat_map(|l| l.split(util::LengthPattern(max_len)))
                       .collect::<Vec<_>>();

        (if let Some(prefix) = prefix {
            med.iter().take(max_lines).map(|l| prefix.to_owned() + l).collect()
        } else {
            med.iter().take(max_lines).map(|l| (*l).to_owned()).collect()
        },
         med.len())
    };
    if ret.is_empty() {
        ret.push("(ok, no output)".to_owned());
    } else if ret.len() < initial_lines {
        if let Some(ref mut l) = ret.last_mut() {
            l.push_str("... (truncated)");
        }
    }
    ret
}

#[allow(boxed_local)] // send_msg is already boxed anyway
fn respond(resp: Response, cfg: OutputCfg, to: MessageSource, send_msg: SendMsgFnBox) {
    let (response, error) = match resp {
        Response::Success(x) => (x, false),
        Response::Error(x) => (x, true),
        _ => return,
    };
    let output = sanitise_output(&response,
                                 if to.is_chan() && !error { Some(&cfg.chan_output_prefix) } else { None },
                                 if to.is_chan() { cfg.max_channel_line_len } else { 425 },
                                 if to.is_chan() { cfg.max_channel_lines } else { cfg.max_priv_lines });
    for line in &output {
        send_msg(to.reply_to(), line);
    }
}
<commit_msg>Add ; give up on terminating cleanly (irc-rs panic bug)<commit_after>#![feature(plugin)]
#![plugin(clippy)]

extern crate irc as irclib;
extern crate evalbotlib as backend;
extern crate rustc_serialize;

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

use backend::{EvalSvc, EvalSvcCfg, Response, util};

use self::irclib::client::server::NetIrcServer;
use self::irclib::client::prelude as ircp;
use self::irclib::client::prelude::{Server, ServerExt};

macro_rules! send_msg {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_privmsg($dest, $line) {
            println!("failed to send message: {}", x)
        }
    };
}

macro_rules! send_notice {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_notice($dest, $line) {
            println!("failed to send notice: {}", x)
        }
    };
}

type EvalWorkerSender = mpsc::Sender<(IrcMessage, SendMsgFnBox)>;
type EvalWorkerReceiver = mpsc::Receiver<(IrcMessage, SendMsgFnBox)>;
type SendMsgFnBox = Box<Fn(&str, &str) + Send>;

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct IrcCfg {
    output: OutputCfg,
    command_prefix: String,
    owners: Vec<String>,
    irc_networks: Vec<ircp::Config>
}

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct OutputCfg {
    chan_output_prefix: String,
    max_channel_lines: usize,
    max_channel_line_len: usize,
    max_priv_lines: usize
}

fn main() {
    let config = match util::decode::<IrcCfg>("evalbot.irc.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.irc.toml: {:?}", x);
            return;
        }
    };
    let (tx, rx) = mpsc::channel();

    {
        let svc = match start_evalsvc() {
            Ok(x) => x,
            Err(()) => return,
        };
        let ocfg = config.output.clone();
        thread::spawn(move || eval_worker(svc, rx, ocfg));
    }

    let (qtx, qrx) = mpsc::channel();

    for (k, nwk) in config.irc_networks
                          .iter()
                          .filter_map(|cfg| {
                              match ircp::IrcServer::from_config(cfg.clone()) {
                                  Ok(conn) => Some(conn),
                                  Err(ref err) => {
                                      println!("failed to create IRC connection from Config {:#?}: {:?}", cfg, err);
                                      None
                                  }
                              }
                          })
                          .enumerate() {
        start_worker(nwk,
                     tx.clone(),
                     config.owners.clone(),
                     config.command_prefix.clone(),
                     qtx.clone(),
                     format!("{}", k));
    }
    util::ignore(qrx.recv());
}

fn start_evalsvc() -> Result<EvalSvc, ()> {
    let evalcfg = match util::decode::<EvalSvcCfg>("evalbot.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.toml: {:?}", x);
            return Err(());
        }
    };
    Ok(EvalSvc::new(evalcfg))
}

fn start_worker(conn: NetIrcServer,
    svc: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String)
                -> thread::JoinHandle<()> {
    thread::spawn(move || {
        worker(conn, svc, owners, cmd_prefix, quit_handle, conn_hash);
    })
}

#[derive(Clone, Debug)]
struct MessageSource {
    chan: Option<String>,
    sender: String,
    conn_hash: String
}

impl MessageSource {
    fn reply_to(&self) -> &str {
        if let Some(ref chan) = self.chan { chan } else { &self.sender }
    }

    fn is_chan(&self) -> bool {
        self.chan.is_some()
    }
}

#[derive(Debug)]
struct IrcMessage {
    sender: MessageSource,
    data: MessageData
}

#[derive(Debug)]
enum MessageData {
    EvalReq {
        lang: String,
        code: String,
        timeout: bool
    },
    Quit,
    Rehash,
    Restart {
        lang: String
    },
    Raw {
        msg: String
    }
}

unsafe impl Sync for IrcMessage {}

fn parse_msg(conn_hash: &str, message: &ircp::Message, owners: &[String], cmd_prefix: &str) -> Option<IrcMessage> {
    let sender = message.source_nickname().unwrap_or("").to_owned();
    if let ircp::Command::PRIVMSG(ref target, ref message) = message.command {
        let target = target.to_owned();
        let is_owner = owners.iter().any(|x| *x == sender);
        let sender = {
            let in_channel = target.starts_with('#');
            if !in_channel && message.contains('\x01') {
                // ignore CTCP
                return None;
            }

            MessageSource {
                chan: if in_channel { Some(target) } else { None },
                sender: sender,
                conn_hash: conn_hash.to_owned()
            }
        };

        if is_owner {
            if let Some(x) = try_cmd(&message, cmd_prefix) {
                return Some(IrcMessage { sender: sender, data: x });
            }
        }

        let tok: Vec<&str> = message.trim().splitn(2, '>').collect();
        if tok.len() == 2 {
            return Some(IrcMessage {
                sender: sender,
                data: MessageData::EvalReq { timeout: true, lang: tok[0].to_owned(), code: tok[1].to_owned() }
            });
        } else if !is_owner {
            return None;
        }

        let tok: Vec<&str> = message.trim().splitn(2, '#').collect();
        if tok.len() == 2 {
            Some(IrcMessage {
                sender: sender,
                data: MessageData::EvalReq { timeout: false, lang: tok[0].to_owned(), code: tok[1].to_owned() }
            })
        } else {
            None
        }
    } else {
        None
    }
}

fn try_cmd(msg: &str, cmd_prefix: &str) -> Option<MessageData> {
    if let Some((cmd, args)) = parse_cmd(msg, cmd_prefix) {
        match &cmd as &str {
            "quit" => Some(MessageData::Quit),
            "rehash" => Some(MessageData::Rehash),
            "restart" if !args.is_empty() => Some(MessageData::Restart { lang: args[0].to_owned() }),
            "raw" if !args.is_empty() => {
                Some(MessageData::Raw {
                    msg: {
                        let mut m = args.join(" ");
                        m.push_str("\r\n");
                        m
                    }
                })
            }
            _ => None,
        }
    } else {
        None
    }
}

fn parse_cmd(msg: &str, cmd_prefix: &str) -> Option<(String, Vec<String>)> {
    let tok: Vec<&str> = match msg.splitn(2, cmd_prefix).nth(1).map(|x| x.split(' ').collect()) {
        Some(x) => x,
        None => return None,
    };

    if tok.is_empty() {
        return None;
    }

    Some((tok[0].to_owned(),
          tok.into_iter().skip(1).map(|x| x.to_owned()).collect::<Vec<String>>()))
}

fn worker(conn: NetIrcServer,
    tx: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String) {
    'connection: loop {
        println!("connecting to to {:?} as {:?}", conn.config().server, conn.config().nickname);
        while let Err(x) = conn.identify() {
            println!("error while identify()ing; retrying: {:?}", x);
            thread::sleep(Duration::new(1, 0));
        }
        println!("connected to {:?} as {:?}", conn.config().server, conn.config().nickname);
        for msg in conn.iter() {
            let msg = match msg {
                Ok(x) => x,
                Err(_) => continue,
            };

            if let Some(msg) = parse_msg(&conn_hash, &msg, &owners, &cmd_prefix) {
                println!("M: {:?}", msg);
                match msg.data {
                    MessageData::EvalReq { .. } | MessageData::Rehash | MessageData::Restart { .. } => {
                        let conn = conn.clone();
                        util::ignore(tx.send((msg, Box::new(move |to, r| send_msg!(conn, to, r)))))
                    }
                    MessageData::Raw { msg: raw_msg } => {
                        match raw_msg.parse::<ircp::Message>() {
                            Ok(raw_msg) => {
                                println!("{} sent raw: {:?}", msg.sender.sender, raw_msg);
                                if let Err(err) = conn.send(raw_msg) {
                                    send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err))
                                }
                            }
                            Err(err) => send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err)),
                        };
                    }
                    MessageData::Quit => {
                        util::ignore(quit_handle.send(()));
                        break 'connection;
                    }
                }
            }
        }
    }
}

fn eval_worker(mut svc: EvalSvc, rx: EvalWorkerReceiver, ocfg: OutputCfg) {
    while let Ok((msg, callback)) = rx.recv() {
        macro_rules! reply {
            ($x:expr) => { callback(msg.sender.reply_to(), $x); }
        }
        match msg.data {
            MessageData::EvalReq { lang, code, timeout } => {
                let ocfg = ocfg.clone();
                let sender = msg.sender.clone();
                svc.eval(&lang,
                         code,
                         timeout,
                         Some(format!("{}{}", msg.sender.conn_hash, msg.sender.reply_to())),
                         Box::new(move |resp| respond(resp, ocfg, sender, callback)))
            }
            MessageData::Rehash => {
                if let Ok(nsvc) = start_evalsvc() {
                    reply!("rehashed");
                    svc = nsvc;
                } else {
                    reply!("read config failed; check logs");
                }
            }
            MessageData::Restart { lang } => {
                match svc.restart(&lang) {
                    Ok(_) => reply!(&format!("restarted {}", lang)),
                    Err(_) => reply!(&format!("no such language {}", lang)),
                }
            }
            _ => println!("invalid thing sent to eval_worker?"),
        };
    }
}

fn sanitise_output(input: &str, prefix: Option<&str>, max_len: usize, max_lines: usize) -> Vec<String> {
    let (mut ret, initial_lines): (Vec<String>, usize) = {
        let med = input.lines()
                       .map(|l| l.trim())
                       .filter(|l| !l.is_empty())
                       .flat_map(|l| l.split(util::LengthPattern(max_len)))
                       .collect::<Vec<_>>();

        (if let Some(prefix) = prefix {
            med.iter().take(max_lines).map(|l| prefix.to_owned() + l).collect()
        } else {
            med.iter().take(max_lines).map(|l| (*l).to_owned()).collect()
        },
         med.len())
    };
    if ret.is_empty() {
        ret.push("(ok, no output)".to_owned());
    } else if ret.len() < initial_lines {
        if let Some(ref mut l) = ret.last_mut() {
            l.push_str("... (truncated)");
        }
    }
    ret
}

#[allow(boxed_local)] // send_msg is already boxed anyway
fn respond(resp: Response, cfg: OutputCfg, to: MessageSource, send_msg: SendMsgFnBox) {
    let (response, error) = match resp {
        Response::Success(x) => (x, false),
        Response::Error(x) => (x, true),
        _ => return,
    };
    let output = sanitise_output(&response,
                                 if to.is_chan() && !error { Some(&cfg.chan_output_prefix) } else { None },
                                 if to.is_chan() { cfg.max_channel_line_len } else { 425 },
                                 if to.is_chan() { cfg.max_channel_lines } else { cfg.max_priv_lines });
    for line in &output {
        send_msg(to.reply_to(), line);
    }
}
<|endoftext|>