<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use font_template::{FontTemplate, FontTemplateDescriptor};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::{AsyncResponseTarget, PendingAsyncLoad, ResourceTask, ResponseAction};
use platform::font_context::FontContextHandle;
use platform::font_list::for_each_available_family;
use platform::font_list::for_each_variation;
use platform::font_list::last_resort_font_families;
use platform::font_list::system_default_family;
use platform::font_template::FontTemplateData;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::mem;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use style::font_face::Source;
use url::Url;
use util::str::LowercaseString;
use util::task::spawn_named;

/// A list of font templates that make up a given font family.
struct FontFamily {
    templates: Vec<FontTemplate>,
}

impl FontFamily {
    fn new() -> FontFamily {
        FontFamily {
            templates: vec!(),
        }
    }

    /// Find a font in this family that matches a given descriptor.
    fn find_font_for_style<'a>(&'a mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
                               -> Option<Arc<FontTemplateData>> {
        // TODO(Issue #189): optimize lookup for
        // regular/bold/italic/bolditalic with fixed offsets and a
        // static decision table for fallback between these values.

        // TODO(Issue #190): if not in the fast path above, do
        // expensive matching of weights, etc.
        for template in &mut self.templates {
            let maybe_template = template.data_for_descriptor(fctx, desc);
            if maybe_template.is_some() {
                return maybe_template;
            }
        }

        // If a request is made for a font family that exists,
        // pick the first valid font in the family if we failed
        // to find an exact match for the descriptor.
        for template in &mut self.templates {
            let maybe_template = template.get();
            if maybe_template.is_some() {
                return maybe_template;
            }
        }

        None
    }

    fn add_template(&mut self, identifier: Atom, maybe_data: Option<Vec<u8>>) {
        for template in &self.templates {
            if *template.identifier() == identifier {
                return;
            }
        }

        let template = FontTemplate::new(identifier, maybe_data);
        self.templates.push(template);
    }
}

/// Commands that the FontContext sends to the font cache task.
pub enum Command {
    GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>),
    GetLastResortFontTemplate(FontTemplateDescriptor, Sender<Reply>),
    AddWebFont(Atom, Source, Sender<()>),
    AddDownloadedWebFont(LowercaseString, Url, Vec<u8>, Sender<()>),
    Exit(Sender<()>),
}

/// Reply messages sent from the font cache task to the FontContext caller.
pub enum Reply {
    GetFontTemplateReply(Option<Arc<FontTemplateData>>),
}

/// The font cache task itself. It maintains a list of reference counted
/// font templates that are currently in use.
struct FontCache {
    port: Receiver<Command>,
    channel_to_self: Sender<Command>,
    generic_fonts: HashMap<LowercaseString, LowercaseString>,
    local_families: HashMap<LowercaseString, FontFamily>,
    web_families: HashMap<LowercaseString, FontFamily>,
    font_context: FontContextHandle,
    resource_task: ResourceTask,
}

fn add_generic_font(generic_fonts: &mut HashMap<LowercaseString, LowercaseString>,
                    generic_name: &str, mapped_name: &str) {
    let opt_system_default = system_default_family(generic_name);
    let family_name = match opt_system_default {
        Some(system_default) => LowercaseString::new(&system_default),
        None => LowercaseString::new(mapped_name),
    };
    generic_fonts.insert(LowercaseString::new(generic_name), family_name);
}

impl FontCache {
    fn run(&mut self) {
        loop {
            let msg = self.port.recv().unwrap();

            match msg {
                Command::GetFontTemplate(family, descriptor, result) => {
                    let family = LowercaseString::new(&family);
                    let maybe_font_template = self.find_font_template(&family, &descriptor);
                    result.send(Reply::GetFontTemplateReply(maybe_font_template)).unwrap();
                }
                Command::GetLastResortFontTemplate(descriptor, result) => {
                    let font_template = self.last_resort_font_template(&descriptor);
                    result.send(Reply::GetFontTemplateReply(Some(font_template))).unwrap();
                }
                Command::AddWebFont(family_name, src, result) => {
                    let family_name = LowercaseString::new(&family_name);
                    if !self.web_families.contains_key(&family_name) {
                        let family = FontFamily::new();
                        self.web_families.insert(family_name.clone(), family);
                    }

                    match src {
                        Source::Url(ref url_source) => {
                            let url = &url_source.url;
                            let load = PendingAsyncLoad::new(self.resource_task.clone(),
                                                             url.clone(),
                                                             None);
                            let (data_sender, data_receiver) = ipc::channel().unwrap();
                            let data_target = AsyncResponseTarget {
                                sender: data_sender,
                            };
                            load.load_async(data_target);
                            let channel_to_self = self.channel_to_self.clone();
                            let url = (*url).clone();
                            let bytes = Mutex::new(Vec::new());
                            ROUTER.add_route(data_receiver.to_opaque(), box move |message| {
                                let response: ResponseAction = message.to().unwrap();
                                match response {
                                    ResponseAction::HeadersAvailable(_) |
                                    ResponseAction::ResponseComplete(Err(_)) => {}
                                    ResponseAction::DataAvailable(new_bytes) => {
                                        bytes.lock().unwrap().extend(new_bytes.into_iter())
                                    }
                                    ResponseAction::ResponseComplete(Ok(_)) => {
                                        let mut bytes = bytes.lock().unwrap();
                                        let bytes = mem::replace(&mut *bytes, Vec::new());
                                        let command =
                                            Command::AddDownloadedWebFont(family_name.clone(),
                                                                          url.clone(),
                                                                          bytes,
                                                                          result.clone());
                                        channel_to_self.send(command).unwrap();
                                    }
                                }
                            });
                        }
                        Source::Local(ref local_family_name) => {
                            let family = &mut self.web_families.get_mut(&family_name).unwrap();
                            for_each_variation(&local_family_name, |path| {
                                family.add_template(Atom::from_slice(&path), None);
                            });
                            result.send(()).unwrap();
                        }
                    }
                }
                Command::AddDownloadedWebFont(family_name, url, bytes, result) => {
                    let family = &mut self.web_families.get_mut(&family_name).unwrap();
                    family.add_template(Atom::from_slice(&url.to_string()), Some(bytes));
                    drop(result.send(()));
                }
                Command::Exit(result) => {
                    result.send(()).unwrap();
                    break;
                }
            }
        }
    }

    fn refresh_local_families(&mut self) {
        self.local_families.clear();
        for_each_available_family(|family_name| {
            let family_name = LowercaseString::new(&family_name);
            if !self.local_families.contains_key(&family_name) {
                let family = FontFamily::new();
                self.local_families.insert(family_name, family);
            }
        });
    }

    fn transform_family(&self, family: &LowercaseString) -> LowercaseString {
        match self.generic_fonts.get(family) {
            None => family.clone(),
            Some(mapped_family) => (*mapped_family).clone()
        }
    }

    fn find_font_in_local_family<'a>(&'a mut self, family_name: &LowercaseString, desc: &FontTemplateDescriptor)
                                -> Option<Arc<FontTemplateData>> {
        // TODO(Issue #188): look up localized font family names if canonical name not found
        // look up canonical name
        if self.local_families.contains_key(family_name) {
            debug!("FontList: Found font family with name={}", &**family_name);
            let s = self.local_families.get_mut(family_name).unwrap();

            if s.templates.is_empty() {
                for_each_variation(family_name, |path| {
                    s.add_template(Atom::from_slice(&path), None);
                });
            }

            // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'.
            // if such family exists, try to match style to a font
            let result = s.find_font_for_style(desc, &self.font_context);
            if result.is_some() {
                return result;
            }

            None
        } else {
            debug!("FontList: Couldn't find font family with name={}", &**family_name);
            None
        }
    }

    fn find_font_in_web_family<'a>(&'a mut self, family_name: &LowercaseString, desc: &FontTemplateDescriptor)
                                -> Option<Arc<FontTemplateData>> {
        if self.web_families.contains_key(family_name) {
            let family = self.web_families.get_mut(family_name).unwrap();
            let maybe_font = family.find_font_for_style(desc, &self.font_context);
            maybe_font
        } else {
            None
        }
    }

    fn find_font_template(&mut self, family: &LowercaseString, desc: &FontTemplateDescriptor)
                            -> Option<Arc<FontTemplateData>> {
        let transformed_family_name = self.transform_family(family);
        let mut maybe_template = self.find_font_in_web_family(&transformed_family_name, desc);
        if maybe_template.is_none() {
            maybe_template = self.find_font_in_local_family(&transformed_family_name, desc);
        }
        maybe_template
    }

    fn last_resort_font_template(&mut self, desc: &FontTemplateDescriptor)
                                        -> Arc<FontTemplateData> {
        let last_resort = last_resort_font_families();

        for family in &last_resort {
            let family = LowercaseString::new(family);
            let maybe_font_in_family = self.find_font_in_local_family(&family, desc);
            if maybe_font_in_family.is_some() {
                return maybe_font_in_family.unwrap();
            }
        }

        panic!("Unable to find any fonts that match (do you have fallback fonts installed?)");
    }
}

/// The public interface to the font cache task, used exclusively by
/// the per-thread/task FontContext structures.
#[derive(Clone)]
pub struct FontCacheTask {
    chan: Sender<Command>,
}

impl FontCacheTask {
    pub fn new(resource_task: ResourceTask) -> FontCacheTask {
        let (chan, port) = channel();

        let channel_to_self = chan.clone();
        spawn_named("FontCacheTask".to_owned(), move || {
            // TODO: Allow users to specify these.
            let mut generic_fonts = HashMap::with_capacity(5);
            add_generic_font(&mut generic_fonts, "serif", "Times New Roman");
            add_generic_font(&mut generic_fonts, "sans-serif", "Arial");
            add_generic_font(&mut generic_fonts, "cursive", "Apple Chancery");
            add_generic_font(&mut generic_fonts, "fantasy", "Papyrus");
            add_generic_font(&mut generic_fonts, "monospace", "Menlo");

            let mut cache = FontCache {
                port: port,
                channel_to_self: channel_to_self,
                generic_fonts: generic_fonts,
                local_families: HashMap::new(),
                web_families: HashMap::new(),
                font_context: FontContextHandle::new(),
                resource_task: resource_task,
            };

            cache.refresh_local_families();
            cache.run();
        });

        FontCacheTask {
            chan: chan,
        }
    }

    pub fn find_font_template(&self, family: String, desc: FontTemplateDescriptor)
                                                -> Option<Arc<FontTemplateData>> {

        let (response_chan, response_port) = channel();
        self.chan.send(Command::GetFontTemplate(family, desc, response_chan)).unwrap();

        let reply = response_port.recv().unwrap();

        match reply {
            Reply::GetFontTemplateReply(data) => {
                data
            }
        }
    }

    pub fn last_resort_font_template(&self, desc: FontTemplateDescriptor)
                                                -> Arc<FontTemplateData> {

        let (response_chan, response_port) = channel();
        self.chan.send(Command::GetLastResortFontTemplate(desc, response_chan)).unwrap();

        let reply = response_port.recv().unwrap();

        match reply {
            Reply::GetFontTemplateReply(data) => {
                data.unwrap()
            }
        }
    }

    pub fn add_web_font(&self, family: Atom, src: Source, sender: Sender<()>) {
        self.chan.send(Command::AddWebFont(family, src, sender)).unwrap();
    }

    pub fn exit(&self) {
        let (response_chan, response_port) = channel();
        self.chan.send(Command::Exit(response_chan)).unwrap();
        response_port.recv().unwrap();
    }
}

<commit_msg>Auto merge of #8493 - notriddle:fct_cleanup, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use font_template::{FontTemplate, FontTemplateDescriptor};
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use net_traits::{AsyncResponseTarget, PendingAsyncLoad, ResourceTask, ResponseAction};
use platform::font_context::FontContextHandle;
use platform::font_list::for_each_available_family;
use platform::font_list::for_each_variation;
use platform::font_list::last_resort_font_families;
use platform::font_list::system_default_family;
use platform::font_template::FontTemplateData;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::mem;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use style::font_face::Source;
use url::Url;
use util::str::LowercaseString;
use util::task::spawn_named;

/// A list of font templates that make up a given font family.
struct FontFamily {
    templates: Vec<FontTemplate>,
}

impl FontFamily {
    fn new() -> FontFamily {
        FontFamily {
            templates: vec!(),
        }
    }

    /// Find a font in this family that matches a given descriptor.
    fn find_font_for_style<'a>(&'a mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
                               -> Option<Arc<FontTemplateData>> {
        // TODO(Issue #189): optimize lookup for
        // regular/bold/italic/bolditalic with fixed offsets and a
        // static decision table for fallback between these values.

        // TODO(Issue #190): if not in the fast path above, do
        // expensive matching of weights, etc.
        for template in &mut self.templates {
            let maybe_template = template.data_for_descriptor(fctx, desc);
            if maybe_template.is_some() {
                return maybe_template;
            }
        }

        // If a request is made for a font family that exists,
        // pick the first valid font in the family if we failed
        // to find an exact match for the descriptor.
        for template in &mut self.templates {
            let maybe_template = template.get();
            if maybe_template.is_some() {
                return maybe_template;
            }
        }

        None
    }

    fn add_template(&mut self, identifier: Atom, maybe_data: Option<Vec<u8>>) {
        for template in &self.templates {
            if *template.identifier() == identifier {
                return;
            }
        }

        let template = FontTemplate::new(identifier, maybe_data);
        self.templates.push(template);
    }
}

/// Commands that the FontContext sends to the font cache task.
pub enum Command {
    GetFontTemplate(String, FontTemplateDescriptor, Sender<Reply>),
    GetLastResortFontTemplate(FontTemplateDescriptor, Sender<Reply>),
    AddWebFont(Atom, Source, Sender<()>),
    AddDownloadedWebFont(LowercaseString, Url, Vec<u8>, Sender<()>),
    Exit(Sender<()>),
}

/// Reply messages sent from the font cache task to the FontContext caller.
pub enum Reply {
    GetFontTemplateReply(Option<Arc<FontTemplateData>>),
}

/// The font cache task itself. It maintains a list of reference counted
/// font templates that are currently in use.
struct FontCache {
    port: Receiver<Command>,
    channel_to_self: Sender<Command>,
    generic_fonts: HashMap<LowercaseString, LowercaseString>,
    local_families: HashMap<LowercaseString, FontFamily>,
    web_families: HashMap<LowercaseString, FontFamily>,
    font_context: FontContextHandle,
    resource_task: ResourceTask,
}

fn add_generic_font(generic_fonts: &mut HashMap<LowercaseString, LowercaseString>,
                    generic_name: &str, mapped_name: &str) {
    let opt_system_default = system_default_family(generic_name);
    let family_name = match opt_system_default {
        Some(system_default) => LowercaseString::new(&system_default),
        None => LowercaseString::new(mapped_name),
    };
    generic_fonts.insert(LowercaseString::new(generic_name), family_name);
}

impl FontCache {
    fn run(&mut self) {
        loop {
            let msg = self.port.recv().unwrap();

            match msg {
                Command::GetFontTemplate(family, descriptor, result) => {
                    let family = LowercaseString::new(&family);
                    let maybe_font_template = self.find_font_template(&family, &descriptor);
                    result.send(Reply::GetFontTemplateReply(maybe_font_template)).unwrap();
                }
                Command::GetLastResortFontTemplate(descriptor, result) => {
                    let font_template = self.last_resort_font_template(&descriptor);
                    result.send(Reply::GetFontTemplateReply(Some(font_template))).unwrap();
                }
                Command::AddWebFont(family_name, src, result) => {
                    let family_name = LowercaseString::new(&family_name);
                    if !self.web_families.contains_key(&family_name) {
                        let family = FontFamily::new();
                        self.web_families.insert(family_name.clone(), family);
                    }

                    match src {
                        Source::Url(ref url_source) => {
                            let url = &url_source.url;
                            let load = PendingAsyncLoad::new(self.resource_task.clone(),
                                                             url.clone(),
                                                             None);
                            let (data_sender, data_receiver) = ipc::channel().unwrap();
                            let data_target = AsyncResponseTarget {
                                sender: data_sender,
                            };
                            load.load_async(data_target);
                            let channel_to_self = self.channel_to_self.clone();
                            let url = (*url).clone();
                            let bytes = Mutex::new(Vec::new());
                            ROUTER.add_route(data_receiver.to_opaque(), box move |message| {
                                let response: ResponseAction = message.to().unwrap();
                                match response {
                                    ResponseAction::HeadersAvailable(_) |
                                    ResponseAction::ResponseComplete(Err(_)) => {}
                                    ResponseAction::DataAvailable(new_bytes) => {
                                        bytes.lock().unwrap().extend(new_bytes.into_iter())
                                    }
                                    ResponseAction::ResponseComplete(Ok(_)) => {
                                        let mut bytes = bytes.lock().unwrap();
                                        let bytes = mem::replace(&mut *bytes, Vec::new());
                                        let command =
                                            Command::AddDownloadedWebFont(family_name.clone(),
                                                                          url.clone(),
                                                                          bytes,
                                                                          result.clone());
                                        channel_to_self.send(command).unwrap();
                                    }
                                }
                            });
                        }
                        Source::Local(ref local_family_name) => {
                            let family = &mut self.web_families.get_mut(&family_name).unwrap();
                            for_each_variation(&local_family_name, |path| {
                                family.add_template(Atom::from_slice(&path), None);
                            });
                            result.send(()).unwrap();
                        }
                    }
                }
                Command::AddDownloadedWebFont(family_name, url, bytes, result) => {
                    let family = &mut self.web_families.get_mut(&family_name).unwrap();
                    family.add_template(Atom::from_slice(&url.to_string()), Some(bytes));
                    drop(result.send(()));
                }
                Command::Exit(result) => {
                    result.send(()).unwrap();
                    break;
                }
            }
        }
    }

    fn refresh_local_families(&mut self) {
        self.local_families.clear();
        for_each_available_family(|family_name| {
            let family_name = LowercaseString::new(&family_name);
            if !self.local_families.contains_key(&family_name) {
                let family = FontFamily::new();
                self.local_families.insert(family_name, family);
            }
        });
    }

    fn transform_family(&self, family: &LowercaseString) -> LowercaseString {
        match self.generic_fonts.get(family) {
            None => family.clone(),
            Some(mapped_family) => (*mapped_family).clone()
        }
    }

    fn find_font_in_local_family<'a>(&'a mut self, family_name: &LowercaseString, desc: &FontTemplateDescriptor)
                                -> Option<Arc<FontTemplateData>> {
        // TODO(Issue #188): look up localized font family names if canonical name not found
        // look up canonical name
        if self.local_families.contains_key(family_name) {
            debug!("FontList: Found font family with name={}", &**family_name);
            let s = self.local_families.get_mut(family_name).unwrap();

            if s.templates.is_empty() {
                for_each_variation(family_name, |path| {
                    s.add_template(Atom::from_slice(&path), None);
                });
            }

            // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'.
            // if such family exists, try to match style to a font

            s.find_font_for_style(desc, &self.font_context)
        } else {
            debug!("FontList: Couldn't find font family with name={}", &**family_name);
            None
        }
    }

    fn find_font_in_web_family<'a>(&'a mut self, family_name: &LowercaseString, desc: &FontTemplateDescriptor)
                                -> Option<Arc<FontTemplateData>> {
        if self.web_families.contains_key(family_name) {
            let family = self.web_families.get_mut(family_name).unwrap();
            let maybe_font = family.find_font_for_style(desc, &self.font_context);
            maybe_font
        } else {
            None
        }
    }

    fn find_font_template(&mut self, family: &LowercaseString, desc: &FontTemplateDescriptor)
                            -> Option<Arc<FontTemplateData>> {
        let transformed_family_name = self.transform_family(family);
        self.find_font_in_web_family(&transformed_family_name, desc)
            .or_else(|| self.find_font_in_local_family(&transformed_family_name, desc))
    }

    fn last_resort_font_template(&mut self, desc: &FontTemplateDescriptor)
                                        -> Arc<FontTemplateData> {
        let last_resort = last_resort_font_families();

        for family in &last_resort {
            let family = LowercaseString::new(family);
            let maybe_font_in_family = self.find_font_in_local_family(&family, desc);
            if let Some(family) = maybe_font_in_family {
                return family;
            }
        }

        panic!("Unable to find any fonts that match (do you have fallback fonts installed?)");
    }
}

/// The public interface to the font cache task, used exclusively by
/// the per-thread/task FontContext structures.
#[derive(Clone)]
pub struct FontCacheTask {
    chan: Sender<Command>,
}

impl FontCacheTask {
    pub fn new(resource_task: ResourceTask) -> FontCacheTask {
        let (chan, port) = channel();

        let channel_to_self = chan.clone();
        spawn_named("FontCacheTask".to_owned(), move || {
            // TODO: Allow users to specify these.
            let mut generic_fonts = HashMap::with_capacity(5);
            add_generic_font(&mut generic_fonts, "serif", "Times New Roman");
            add_generic_font(&mut generic_fonts, "sans-serif", "Arial");
            add_generic_font(&mut generic_fonts, "cursive", "Apple Chancery");
            add_generic_font(&mut generic_fonts, "fantasy", "Papyrus");
            add_generic_font(&mut generic_fonts, "monospace", "Menlo");

            let mut cache = FontCache {
                port: port,
                channel_to_self: channel_to_self,
                generic_fonts: generic_fonts,
                local_families: HashMap::new(),
                web_families: HashMap::new(),
                font_context: FontContextHandle::new(),
                resource_task: resource_task,
            };

            cache.refresh_local_families();
            cache.run();
        });

        FontCacheTask {
            chan: chan,
        }
    }

    pub fn find_font_template(&self, family: String, desc: FontTemplateDescriptor)
                                                -> Option<Arc<FontTemplateData>> {

        let (response_chan, response_port) = channel();
        self.chan.send(Command::GetFontTemplate(family, desc, response_chan)).unwrap();

        let reply = response_port.recv().unwrap();

        match reply {
            Reply::GetFontTemplateReply(data) => {
                data
            }
        }
    }

    pub fn last_resort_font_template(&self, desc: FontTemplateDescriptor)
                                                -> Arc<FontTemplateData> {

        let (response_chan, response_port) = channel();
        self.chan.send(Command::GetLastResortFontTemplate(desc, response_chan)).unwrap();

        let reply = response_port.recv().unwrap();

        match reply {
            Reply::GetFontTemplateReply(data) => {
                data.unwrap()
            }
        }
    }

    pub fn add_web_font(&self, family: Atom, src: Source, sender: Sender<()>) {
        self.chan.send(Command::AddWebFont(family, src, sender)).unwrap();
    }

    pub fn exit(&self) {
        let (response_chan, response_port) = channel();
        self.chan.send(Command::Exit(response_chan)).unwrap();
        response_port.recv().unwrap();
    }
}

<|endoftext|>


<commit_before>extern crate protobuf_codegen_pure;

use std::env;
use std::path::PathBuf;

fn main() {
    let args = env::args_os()
        .skip(1)
        .map(PathBuf::from)
        .collect::<Vec<_>>();
    assert!(args.len() >= 2);
    let (input, includes) = args.split_at(1);
    let t =
        protobuf_codegen_pure::parse_and_typecheck(includes, input).expect("parse_and_typecheck");
    for fd in t.file_descriptors {
        println!("{:#?}", fd);
    }
}
<commit_msg>parse-and-typecheck: better error message on incorrect input<commit_after>extern crate protobuf_codegen_pure;

use std::env;
use std::path::PathBuf;
use std::process::exit;

fn main() {
    let args = env::args_os()
        .skip(1)
        .map(PathBuf::from)
        .collect::<Vec<_>>();

    if args.len() != 2 {
        eprintln!(
            "usage: {} <input.proto> <include>",
            env::args().next().unwrap()
        );
        exit(1);
    }

    assert!(args.len() >= 2);
    let (input, includes) = args.split_at(1);
    let t =
        protobuf_codegen_pure::parse_and_typecheck(includes, input).expect("parse_and_typecheck");
    for fd in t.file_descriptors {
        println!("{:#?}", fd);
    }
}
<|endoftext|>


<commit_before>//! An extension trait that provides the ability to strip IRC colors from a string
use std::borrow::Cow;

#[derive(PartialEq)]
enum ParserState {
    Text,
    ColorCode,
    Foreground1,
    Foreground2,
    Comma,
    Background1,
}
struct Parser {
    state: ParserState,
}

/// An extension trait giving strings a function to strip IRC colors
pub trait FormattedStringExt {

    /// Returns true if the string contains color, bold, underline or italics
    fn is_formatted(&self) -> bool;

    /// Returns the string with all color, bold, underline and italics stripped
    fn strip_formatting(&self) -> Cow<str>;

}

impl FormattedStringExt for str {
    fn is_formatted(&self) -> bool {
        self.contains('\x02') || // bold
            self.contains('\x1F') || // underline
            self.contains('\x16') || // reverse
            self.contains('\x0F') || // normal
            self.contains('\x03') // color
    }

    fn strip_formatting(&self) -> Cow<str> {
        let mut parser = Parser {
            state: ParserState::Text,
        };
        let mut prev: char = '\x00';
        let result: Cow<str> = self
            .chars()
            .filter(move |cur| {
                let result = match parser.state {
                    ParserState::Text | ParserState::Foreground1 | ParserState::Foreground2 if *cur == '\x03' => {
                        parser.state = ParserState::ColorCode;
                        false
                    },
                    ParserState::Text => !['\x02', '\x1F', '\x16', '\x0F'].contains(cur),
                    ParserState::ColorCode if  (*cur).is_digit(10) => {
                        parser.state = ParserState::Foreground1;
                        false
                    },
                    ParserState::Foreground1 if (*cur).is_digit(6) => {
                        // can only consume another digit if previous char was 1.
                        if (prev) == '1' {
                            parser.state = ParserState::Foreground2;
                            false
                        } else {
                            parser.state = ParserState::Text;
                            true
                        }
                    },
                    ParserState::Foreground1 if *cur == ','  => {
                        parser.state = ParserState::Comma;
                        false
                    },
                    ParserState::Foreground2 if *cur == ',' => {
                        parser.state = ParserState::Comma;
                        false
                    },
                    ParserState::Comma if ((*cur).is_digit(10)) => {
                        parser.state = ParserState::Background1;
                        false
                    },
                    ParserState::Background1 if (*cur).is_digit(6) => {
                        // can only consume another digit if previous char was 1.
                        parser.state = ParserState::Text;
                        if (prev) == '1' {
                            false
                        } else {
                            true
                        }
                    }
                    _ => {
                        parser.state = ParserState::Text;
                        true
                    }
                };
                prev = *cur;
                return result
            })
            .collect();

        result
    }

}

impl FormattedStringExt for String {
    fn is_formatted(&self) -> bool {
        (&self[..]).is_formatted()
    }
    fn strip_formatting(&self) -> Cow<str> {
        (&self[..]).strip_formatting()
    }
}

#[cfg(test)]
mod test {
    use proto::colors::FormattedStringExt;

    #[test]
    fn test_strip_bold() {
        assert_eq!("l\x02ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_color() {
        assert_eq!("l\x033ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_color2() {
        assert_eq!("l\x0312ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_bg_11() {
        assert_eq!("l\x031,2ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_21() {
        assert_eq!("l\x0312,3ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_12() {
        assert_eq!("l\x031,12ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_22() {
        assert_eq!("l\x0312,13ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_string_with_multiple_colors() {
        assert_eq!("hoo\x034r\x033a\x0312y".strip_formatting(), "hooray");
    }
    #[test]
    fn test_strip_string_with_digit_after_color() {
        assert_eq!("\x0344\x0355\x0366".strip_formatting(), "456");
    }
    #[test]
    fn test_strip_string_with_multiple_2digit_colors() {
        assert_eq!("hoo\x0310r\x0311a\x0312y".strip_formatting(), "hooray");
    }
    #[test]
    fn test_strip_string_with_digit_after_2digit_color() {
        assert_eq!("\x031212\x031111\x031010".strip_formatting(), "121110");
    }
}<commit_msg>Remove unnecessary [..]<commit_after>//! An extension trait that provides the ability to strip IRC colors from a string
use std::borrow::Cow;

#[derive(PartialEq)]
enum ParserState {
    Text,
    ColorCode,
    Foreground1,
    Foreground2,
    Comma,
    Background1,
}
struct Parser {
    state: ParserState,
}

/// An extension trait giving strings a function to strip IRC colors
pub trait FormattedStringExt {

    /// Returns true if the string contains color, bold, underline or italics
    fn is_formatted(&self) -> bool;

    /// Returns the string with all color, bold, underline and italics stripped
    fn strip_formatting(&self) -> Cow<str>;

}

impl FormattedStringExt for str {
    fn is_formatted(&self) -> bool {
        self.contains('\x02') || // bold
            self.contains('\x1F') || // underline
            self.contains('\x16') || // reverse
            self.contains('\x0F') || // normal
            self.contains('\x03') // color
    }

    fn strip_formatting(&self) -> Cow<str> {
        let mut parser = Parser {
            state: ParserState::Text,
        };
        let mut prev: char = '\x00';
        let result: Cow<str> = self
            .chars()
            .filter(move |cur| {
                let result = match parser.state {
                    ParserState::Text | ParserState::Foreground1 | ParserState::Foreground2 if *cur == '\x03' => {
                        parser.state = ParserState::ColorCode;
                        false
                    },
                    ParserState::Text => !['\x02', '\x1F', '\x16', '\x0F'].contains(cur),
                    ParserState::ColorCode if  (*cur).is_digit(10) => {
                        parser.state = ParserState::Foreground1;
                        false
                    },
                    ParserState::Foreground1 if (*cur).is_digit(6) => {
                        // can only consume another digit if previous char was 1.
                        if (prev) == '1' {
                            parser.state = ParserState::Foreground2;
                            false
                        } else {
                            parser.state = ParserState::Text;
                            true
                        }
                    },
                    ParserState::Foreground1 if *cur == ','  => {
                        parser.state = ParserState::Comma;
                        false
                    },
                    ParserState::Foreground2 if *cur == ',' => {
                        parser.state = ParserState::Comma;
                        false
                    },
                    ParserState::Comma if ((*cur).is_digit(10)) => {
                        parser.state = ParserState::Background1;
                        false
                    },
                    ParserState::Background1 if (*cur).is_digit(6) => {
                        // can only consume another digit if previous char was 1.
                        parser.state = ParserState::Text;
                        if (prev) == '1' {
                            false
                        } else {
                            true
                        }
                    }
                    _ => {
                        parser.state = ParserState::Text;
                        true
                    }
                };
                prev = *cur;
                return result
            })
            .collect();

        result
    }

}

impl FormattedStringExt for String {
    fn is_formatted(&self) -> bool {
        (&self).is_formatted()
    }
    fn strip_formatting(&self) -> Cow<str> {
        (&self).strip_formatting()
    }
}

#[cfg(test)]
mod test {
    use proto::colors::FormattedStringExt;

    #[test]
    fn test_strip_bold() {
        assert_eq!("l\x02ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_color() {
        assert_eq!("l\x033ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_color2() {
        assert_eq!("l\x0312ol".strip_formatting(), "lol");
    }

    #[test]
    fn test_strip_fg_bg_11() {
        assert_eq!("l\x031,2ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_21() {
        assert_eq!("l\x0312,3ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_12() {
        assert_eq!("l\x031,12ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_fg_bg_22() {
        assert_eq!("l\x0312,13ol".strip_formatting(), "lol");
    }
    #[test]
    fn test_strip_string_with_multiple_colors() {
        assert_eq!("hoo\x034r\x033a\x0312y".strip_formatting(), "hooray");
    }
    #[test]
    fn test_strip_string_with_digit_after_color() {
        assert_eq!("\x0344\x0355\x0366".strip_formatting(), "456");
    }
    #[test]
    fn test_strip_string_with_multiple_2digit_colors() {
        assert_eq!("hoo\x0310r\x0311a\x0312y".strip_formatting(), "hooray");
    }
    #[test]
    fn test_strip_string_with_digit_after_2digit_color() {
        assert_eq!("\x031212\x031111\x031010".strip_formatting(), "121110");
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/// This manages registration of the foxbox with the discovery endpoint.
/// For now it simply register itselfs every N minutes with the endpoint,
/// after trying more aggressively at first run.

extern crate get_if_addrs;
extern crate hyper;

use self::hyper::Client;
use self::hyper::header::Connection;
use self::hyper::status::StatusCode;
use self::get_if_addrs::{ IfAddr, Interface };
use foxbox_core::traits::Controller;
use serde_json;
use std::io::Read;
use std::time::Duration;
use std::thread;
use tls::{ CertificateManager, DnsRecord, get_san_cert_for, register_dns_record };
use tunnel_controller:: { Tunnel };

const REGISTRATION_INTERVAL_IN_MINUTES: u32 = 1;

pub struct Registrar {
    certificate_manager: CertificateManager,
    top_level_domain: String,
    registration_endpoint: String,
    dns_api_endpoint: String,
}

#[derive(Serialize, Debug)]
struct RegistrationRequest {
    message: String,
    client: String,

    local_ip: String,
}

impl Registrar {
    pub fn new(certificate_manager: CertificateManager,
               top_level_domain: String,
               registration_endpoint: String,
               dns_api_endpoint: String) -> Registrar {
        Registrar {
            certificate_manager: certificate_manager,
            top_level_domain: top_level_domain,
            registration_endpoint: format!("{}/register", registration_endpoint),
            dns_api_endpoint: dns_api_endpoint,
        }
    }

    fn get_fingerprint(&self) -> String {
        self.certificate_manager.get_box_certificate()
                                .unwrap()
                                .get_certificate_fingerprint()
    }

    fn get_common_name(&self) -> String {
        format!("{}.{}", self.get_fingerprint(), self.top_level_domain)
    }

    pub fn get_local_dns_name(&self) -> String {
        format!("local.{}", self.get_common_name())
    }

    pub fn get_remote_dns_name(&self) -> String {
        format!("remote.{}", self.get_common_name())
    }

    fn register_with_registration_server(&self, ip_addr: String, http_scheme: &str, box_port: u16, tunnel_enabled: bool) -> () {
        let message = json!({
            local_origin: format!("{}://{}:{}", http_scheme, self.get_local_dns_name(), box_port),
            tunnel_origin: if tunnel_enabled {
                Some(format!("{}://{}", http_scheme, self.get_remote_dns_name()))
            } else {
                None
            }
        });

        let body = match serde_json::to_string(&RegistrationRequest {
            message: message,
            client: self.get_fingerprint(),
            local_ip: ip_addr,
        }) {
            Ok(body) => body,
            Err(_) => {
                error!("registration server: Serialization error. Will not send registration request.");
                return;
            }
        };

        let client = Client::new();
        let res = client.post(&self.registration_endpoint)
            .header(Connection::close())
            .body(&body)
            .send();

        // Sanity checks, mostly to debug errors since we don't try
        // to recover from failures.
        if let Ok(mut response) = res {
            if response.status == StatusCode::Ok {
                let mut body = String::new();
                if let Ok(_) = response.read_to_string(&mut body) {
                    info!("registration server responded with: {}", body);
                } else {
                    warn!("registration server: Unable to read answer from {}", self.registration_endpoint);
                }
            }
        } else {
            warn!("registration server: Unable to send request to {}", self.registration_endpoint);
        }
    }

    /// Registers the boxes local IP address as an A record with the DNS server, and
    /// registers a CNAME record for the tunnel endpoint using the box's assigned
    /// names (local.<fingerprint>.box.knilxof.org and
    /// remote.<fingerprint>.box.knilxof.org).  The remote name (tunnel name), is
    /// only configured if the tunnel_frontend option is non-None.
    fn register_with_dns_server(&self, ip_addr: String, tunnel_frontend: Option<String>) {
        let client_certificate = self.certificate_manager.get_box_certificate().unwrap();

        let local_name = self.get_local_dns_name();
        // Create entry for local DNS
        info!("DNS server: Creating DNS entry for {}", local_name);
        let result = register_dns_record(
            client_certificate.clone(),
            &DnsRecord {
                record_type: "A",
                name: &local_name,
                value: &ip_addr,
            },
            &self.dns_api_endpoint.clone(),
        );

        if let Err(_) = result {
            warn!("DNS server: Could not create DNS entry for {}", local_name);
        }

        if let Some(tunnel_frontend) = tunnel_frontend {
            let remote_name = self.get_remote_dns_name();
            info!("DNS server: Creating DNS entry for {}", remote_name);
            let result = register_dns_record(
                client_certificate.clone(),
                &DnsRecord {
                    record_type: "CNAME",
                    name: &remote_name,
                    value: &tunnel_frontend,
                },
                &self.dns_api_endpoint.clone(),
            );

            if let Err(_) = result {
                warn!("DNS server: Could not create DNS entry for {}", remote_name);
            }
        }
    }

    fn register_certificates(&self) {
        if self.certificate_manager.get_certificate(&self.get_local_dns_name()).is_none() {
            let domains = vec![self.get_local_dns_name(), self.get_remote_dns_name()];

            info!("Getting/renewing LetsEncrypt certificate for: {:?}", domains);
            let rx = get_san_cert_for(
                domains.into_iter(),
                self.certificate_manager.clone(),
                self.dns_api_endpoint.clone()
            );

            rx.recv().unwrap().unwrap();
            self.certificate_manager.reload().unwrap();
        }
    }

    pub fn start<T: Controller>(self,
                                iface: Option<String>,
                                tunnel: &Option<Tunnel>,
                                box_port: u16,
                                controller: &T) {
        info!("registration server: Starting registration with {}",
                self.registration_endpoint);

        let ip_addr = self.get_ip_addr(&iface);
        if ip_addr == None {
            // TODO: retry later, in case we're racing with the network
            // configuration. https://github.com/fxbox/foxbox/issues/347
            return;
        }

        info!("Got ip address: {}", ip_addr.clone().unwrap());

        let tunnel_frontend = if let Some(ref tunnel) = *tunnel {
            tunnel.get_frontend_name()
        } else {
            None
        };
        let enabled_tls = controller.get_tls_enabled();

        let http_scheme = if enabled_tls {
            "https"
        } else {
            "http"
        };

        // Spawn a thread to register every REGISTRATION_INTERVAL_IN_MINUTES.
        thread::Builder::new().name("Registrar".to_owned())
            .spawn(move || {
                let tunnel_configured = tunnel_frontend.clone().is_some();

                if enabled_tls {
                    self.register_certificates();
                }

                loop {
                    // TODO: If the ip address changes, we need to update the dns server and
                    // registration server with the new IP address.
                    // https://github.com/fxbox/foxbox/issues/348
                    self.register_with_registration_server(
                        ip_addr.clone().unwrap(),
                        http_scheme,
                        box_port,
                        tunnel_configured
                    );
                    self.register_with_dns_server(ip_addr.clone().unwrap(), tunnel_frontend.clone());

                    // Go to sleep.
                    thread::sleep(Duration::from_secs(REGISTRATION_INTERVAL_IN_MINUTES as u64 * 60))
                }
            }).unwrap();
    }

    /// return the host IP address of the first valid interface.
    /// want_iface is an options string for the interface you want.
    pub fn get_ip_addr(&self, want_iface: &Option<String>) -> Option<String> {
        // Look for an ipv4 interface on eth* or wlan*.
        if let Ok(ifaces) = get_if_addrs::get_if_addrs() {
            if ifaces.is_empty() {
                error!("No IP interfaces found!");
                return None;
            }

            self.get_ip_addr_from_ifaces(&ifaces, want_iface)
        } else {
            error!("No IP interfaces found!");
            None
        }
    }

    /// This is a private function that to which we pass the ifaces
    /// This is so that we can shim get_if_addrs() in tests with a
    /// pre-set list of interfaces.
    fn get_ip_addr_from_ifaces(&self, ifaces: &[Interface],
                               want_iface: &Option<String>) -> Option<String> {

        let mut ip_addr: Option<String> = None;
        let mut ipv6_addr: Option<String> = None;

        for iface in ifaces {
            match want_iface.as_ref() {
                None =>
                    // Whitelist known good iface
                    if !(iface.name.starts_with("eth") ||
                         iface.name.starts_with("wlan") ||
                         iface.name.starts_with("en") ||
                         iface.name.starts_with("em") ||
                         iface.name.starts_with("wlp3s")) {
                        continue;
                    },
                    Some(iface_name) =>
                        if &iface.name != iface_name {
                            continue;
                        }
            }
            if let IfAddr::V4(ref v4) = iface.addr {
                ip_addr = Some(format!("{}", v4.ip));
                break;
            } else if ipv6_addr.is_none() {
                if let IfAddr::V6(ref v6) = iface.addr {
                    ipv6_addr = Some(format!("{}", v6.ip));
                }
            }
        }

        if ip_addr.is_none() {
            if ipv6_addr.is_none() {
                error!("No IP interfaces found!");
            } else {
                ip_addr = ipv6_addr;
            }
        }
        ip_addr
    }

}

#[cfg(test)]
describe! registrar {

    before_each {
        use std::path::PathBuf;
        use tls::{ CertificateManager, SniSslContextProvider };
        let registrar = Registrar::new(
            CertificateManager::new(PathBuf::from(current_dir!()), Box::new(SniSslContextProvider::new())),
            "box.knilxof.org".to_owned(),
            "https://knilxof.org:4443/".to_owned(),
            "https://knilxof.org:5300".to_owned()
        );
    }

    it "should return an IP address when a machine has network interfaces" {
        use regex::Regex;

        let ip = registrar.get_ip_addr(&None).unwrap();
        let ipv4_regex = Regex::new(r"^(\d{1,3}\.){3}\d{1,3}$").unwrap();
        assert!(ipv4_regex.is_match(ip.as_str()));
    }

    describe! ipv4 {
        before_each {
            use super::super::get_if_addrs::*;
            use std::net::Ipv4Addr;

            let interfaces: Vec<Interface> = vec![
                Interface {
                    name: "lo".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(127,0,0,1),
                        netmask: Ipv4Addr::new(255,0,0,0),
                        broadcast: None
                    })
                },
                Interface {
                    name: "docker0".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(172,18,1,42),
                        netmask: Ipv4Addr::new(255,255,255,0),
                        broadcast: None
                    })
                },
                Interface {
                    name: "eth0".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(192,168,0,4),
                        netmask: Ipv4Addr::new(255,255,255,0),
                        broadcast: Some(Ipv4Addr::new(192,160,0,255))
                    })
                },
                Interface {
                    name: "wlan0".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(192,168,0,14),
                        netmask: Ipv4Addr::new(255,255,255,0),
                        broadcast: Some(Ipv4Addr::new(192,160,0,255))
                    })
                }
            ];
        }

        it "should default to eth0" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &None).unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }

        it "should retrieve address from eth0" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("eth0".to_owned()))
                .unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }

        it "should retrieve address from wlan0" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("wlan0".to_owned()))
                .unwrap();
            assert_eq!(ip_addr, "192.168.0.14");
        }

        it "should retrieve address from docker0" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("docker0".to_owned())).unwrap();
            assert_eq!(ip_addr, "172.18.1.42");
        }
    }

    describe! ipv6 {
        before_each {
            use super::super::get_if_addrs::*;
            use std::net::{ Ipv4Addr, Ipv6Addr };

            let interfaces: Vec<Interface> = vec![
                Interface {
                    name: "eth0".to_owned(),
                    addr: IfAddr::V6(Ifv6Addr {
                        ip: Ipv6Addr::new(0,0,0,0,0,0xffff,0xc0a8,0x4),
                        netmask: Ipv6Addr::new(0xffff,0xffff,0xffff,0xffff,
                                               0xffff,0xffff,0xffff,0xffff),
                        broadcast: None
                    })
                },
                Interface {
                    name: "eth0".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(192,168,0,4),
                        netmask: Ipv4Addr::new(255,255,255,0),
                        broadcast: Some(Ipv4Addr::new(192,160,0,255))
                    })
                },
                Interface {
                    name: "eth1".to_owned(),
                    addr: IfAddr::V6(Ifv6Addr {
                        ip: Ipv6Addr::new(0,0,0,0,0,0xffff,0xc0a8,0x4),
                        netmask: Ipv6Addr::new(0xffff,0xffff,0xffff,0xffff,
                                               0xffff,0xffff,0xffff,0xffff),
                        broadcast: None
                    })
                }
            ];
        }

        it "should return default IPv4" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &None)
                .unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }

        it "should return IPv6" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("eth1".to_owned()))
                .unwrap();
            assert_eq!(ip_addr, "::ffff:192.168.0.4");
        }

        it "should return IPv4 if both are specified" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("eth0".to_owned()))
                .unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }

        it "should return IPv4 and eth0 by default" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &None).unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }
    }
}
<commit_msg>Add wlp4s to the list of well known network interfaces.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/// This manages registration of the foxbox with the discovery endpoint.
/// For now it simply register itselfs every N minutes with the endpoint,
/// after trying more aggressively at first run.

extern crate get_if_addrs;
extern crate hyper;

use self::hyper::Client;
use self::hyper::header::Connection;
use self::hyper::status::StatusCode;
use self::get_if_addrs::{ IfAddr, Interface };
use foxbox_core::traits::Controller;
use serde_json;
use std::io::Read;
use std::time::Duration;
use std::thread;
use tls::{ CertificateManager, DnsRecord, get_san_cert_for, register_dns_record };
use tunnel_controller:: { Tunnel };

const REGISTRATION_INTERVAL_IN_MINUTES: u32 = 1;

pub struct Registrar {
    certificate_manager: CertificateManager,
    top_level_domain: String,
    registration_endpoint: String,
    dns_api_endpoint: String,
}

#[derive(Serialize, Debug)]
struct RegistrationRequest {
    message: String,
    client: String,

    local_ip: String,
}

impl Registrar {
    pub fn new(certificate_manager: CertificateManager,
               top_level_domain: String,
               registration_endpoint: String,
               dns_api_endpoint: String) -> Registrar {
        Registrar {
            certificate_manager: certificate_manager,
            top_level_domain: top_level_domain,
            registration_endpoint: format!("{}/register", registration_endpoint),
            dns_api_endpoint: dns_api_endpoint,
        }
    }

    fn get_fingerprint(&self) -> String {
        self.certificate_manager.get_box_certificate()
                                .unwrap()
                                .get_certificate_fingerprint()
    }

    fn get_common_name(&self) -> String {
        format!("{}.{}", self.get_fingerprint(), self.top_level_domain)
    }

    pub fn get_local_dns_name(&self) -> String {
        format!("local.{}", self.get_common_name())
    }

    pub fn get_remote_dns_name(&self) -> String {
        format!("remote.{}", self.get_common_name())
    }

    fn register_with_registration_server(&self, ip_addr: String, http_scheme: &str, box_port: u16, tunnel_enabled: bool) -> () {
        let message = json!({
            local_origin: format!("{}://{}:{}", http_scheme, self.get_local_dns_name(), box_port),
            tunnel_origin: if tunnel_enabled {
                Some(format!("{}://{}", http_scheme, self.get_remote_dns_name()))
            } else {
                None
            }
        });

        let body = match serde_json::to_string(&RegistrationRequest {
            message: message,
            client: self.get_fingerprint(),
            local_ip: ip_addr,
        }) {
            Ok(body) => body,
            Err(_) => {
                error!("registration server: Serialization error. Will not send registration request.");
                return;
            }
        };

        let client = Client::new();
        let res = client.post(&self.registration_endpoint)
            .header(Connection::close())
            .body(&body)
            .send();

        // Sanity checks, mostly to debug errors since we don't try
        // to recover from failures.
        if let Ok(mut response) = res {
            if response.status == StatusCode::Ok {
                let mut body = String::new();
                if let Ok(_) = response.read_to_string(&mut body) {
                    info!("registration server responded with: {}", body);
                } else {
                    warn!("registration server: Unable to read answer from {}", self.registration_endpoint);
                }
            }
        } else {
            warn!("registration server: Unable to send request to {}", self.registration_endpoint);
        }
    }

    /// Registers the boxes local IP address as an A record with the DNS server, and
    /// registers a CNAME record for the tunnel endpoint using the box's assigned
    /// names (local.<fingerprint>.box.knilxof.org and
    /// remote.<fingerprint>.box.knilxof.org).  The remote name (tunnel name), is
    /// only configured if the tunnel_frontend option is non-None.
    fn register_with_dns_server(&self, ip_addr: String, tunnel_frontend: Option<String>) {
        let client_certificate = self.certificate_manager.get_box_certificate().unwrap();

        let local_name = self.get_local_dns_name();
        // Create entry for local DNS
        info!("DNS server: Creating DNS entry for {}", local_name);
        let result = register_dns_record(
            client_certificate.clone(),
            &DnsRecord {
                record_type: "A",
                name: &local_name,
                value: &ip_addr,
            },
            &self.dns_api_endpoint.clone(),
        );

        if let Err(_) = result {
            warn!("DNS server: Could not create DNS entry for {}", local_name);
        }

        if let Some(tunnel_frontend) = tunnel_frontend {
            let remote_name = self.get_remote_dns_name();
            info!("DNS server: Creating DNS entry for {}", remote_name);
            let result = register_dns_record(
                client_certificate.clone(),
                &DnsRecord {
                    record_type: "CNAME",
                    name: &remote_name,
                    value: &tunnel_frontend,
                },
                &self.dns_api_endpoint.clone(),
            );

            if let Err(_) = result {
                warn!("DNS server: Could not create DNS entry for {}", remote_name);
            }
        }
    }

    fn register_certificates(&self) {
        if self.certificate_manager.get_certificate(&self.get_local_dns_name()).is_none() {
            let domains = vec![self.get_local_dns_name(), self.get_remote_dns_name()];

            info!("Getting/renewing LetsEncrypt certificate for: {:?}", domains);
            let rx = get_san_cert_for(
                domains.into_iter(),
                self.certificate_manager.clone(),
                self.dns_api_endpoint.clone()
            );

            rx.recv().unwrap().unwrap();
            self.certificate_manager.reload().unwrap();
        }
    }

    pub fn start<T: Controller>(self,
                                iface: Option<String>,
                                tunnel: &Option<Tunnel>,
                                box_port: u16,
                                controller: &T) {
        info!("registration server: Starting registration with {}",
                self.registration_endpoint);

        let ip_addr = self.get_ip_addr(&iface);
        if ip_addr == None {
            // TODO: retry later, in case we're racing with the network
            // configuration. https://github.com/fxbox/foxbox/issues/347
            return;
        }

        info!("Got ip address: {}", ip_addr.clone().unwrap());

        let tunnel_frontend = if let Some(ref tunnel) = *tunnel {
            tunnel.get_frontend_name()
        } else {
            None
        };
        let enabled_tls = controller.get_tls_enabled();

        let http_scheme = if enabled_tls {
            "https"
        } else {
            "http"
        };

        // Spawn a thread to register every REGISTRATION_INTERVAL_IN_MINUTES.
        thread::Builder::new().name("Registrar".to_owned())
            .spawn(move || {
                let tunnel_configured = tunnel_frontend.clone().is_some();

                if enabled_tls {
                    self.register_certificates();
                }

                loop {
                    // TODO: If the ip address changes, we need to update the dns server and
                    // registration server with the new IP address.
                    // https://github.com/fxbox/foxbox/issues/348
                    self.register_with_registration_server(
                        ip_addr.clone().unwrap(),
                        http_scheme,
                        box_port,
                        tunnel_configured
                    );
                    self.register_with_dns_server(ip_addr.clone().unwrap(), tunnel_frontend.clone());

                    // Go to sleep.
                    thread::sleep(Duration::from_secs(REGISTRATION_INTERVAL_IN_MINUTES as u64 * 60))
                }
            }).unwrap();
    }

    /// return the host IP address of the first valid interface.
    /// want_iface is an options string for the interface you want.
    pub fn get_ip_addr(&self, want_iface: &Option<String>) -> Option<String> {
        // Look for an ipv4 interface on eth* or wlan*.
        if let Ok(ifaces) = get_if_addrs::get_if_addrs() {
            if ifaces.is_empty() {
                error!("No IP interfaces found!");
                return None;
            }

            self.get_ip_addr_from_ifaces(&ifaces, want_iface)
        } else {
            error!("No IP interfaces found!");
            None
        }
    }

    /// This is a private function that to which we pass the ifaces
    /// This is so that we can shim get_if_addrs() in tests with a
    /// pre-set list of interfaces.
    fn get_ip_addr_from_ifaces(&self, ifaces: &[Interface],
                               want_iface: &Option<String>) -> Option<String> {

        let mut ip_addr: Option<String> = None;
        let mut ipv6_addr: Option<String> = None;

        for iface in ifaces {
            match want_iface.as_ref() {
                None =>
                    // Whitelist known good iface
                    if !(iface.name.starts_with("eth") ||
                         iface.name.starts_with("wlan") ||
                         iface.name.starts_with("en") ||
                         iface.name.starts_with("em") ||
                         iface.name.starts_with("wlp3s") ||
                         iface.name.starts_with("wlp4s")) {
                        continue;
                    },
                    Some(iface_name) =>
                        if &iface.name != iface_name {
                            continue;
                        }
            }
            if let IfAddr::V4(ref v4) = iface.addr {
                ip_addr = Some(format!("{}", v4.ip));
                break;
            } else if ipv6_addr.is_none() {
                if let IfAddr::V6(ref v6) = iface.addr {
                    ipv6_addr = Some(format!("{}", v6.ip));
                }
            }
        }

        if ip_addr.is_none() {
            if ipv6_addr.is_none() {
                error!("No IP interfaces found!");
            } else {
                ip_addr = ipv6_addr;
            }
        }
        ip_addr
    }

}

#[cfg(test)]
describe! registrar {

    before_each {
        use std::path::PathBuf;
        use tls::{ CertificateManager, SniSslContextProvider };
        let registrar = Registrar::new(
            CertificateManager::new(PathBuf::from(current_dir!()), Box::new(SniSslContextProvider::new())),
            "box.knilxof.org".to_owned(),
            "https://knilxof.org:4443/".to_owned(),
            "https://knilxof.org:5300".to_owned()
        );
    }

    it "should return an IP address when a machine has network interfaces" {
        use regex::Regex;

        let ip = registrar.get_ip_addr(&None).unwrap();
        let ipv4_regex = Regex::new(r"^(\d{1,3}\.){3}\d{1,3}$").unwrap();
        assert!(ipv4_regex.is_match(ip.as_str()));
    }

    describe! ipv4 {
        before_each {
            use super::super::get_if_addrs::*;
            use std::net::Ipv4Addr;

            let interfaces: Vec<Interface> = vec![
                Interface {
                    name: "lo".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(127,0,0,1),
                        netmask: Ipv4Addr::new(255,0,0,0),
                        broadcast: None
                    })
                },
                Interface {
                    name: "docker0".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(172,18,1,42),
                        netmask: Ipv4Addr::new(255,255,255,0),
                        broadcast: None
                    })
                },
                Interface {
                    name: "eth0".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(192,168,0,4),
                        netmask: Ipv4Addr::new(255,255,255,0),
                        broadcast: Some(Ipv4Addr::new(192,160,0,255))
                    })
                },
                Interface {
                    name: "wlan0".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(192,168,0,14),
                        netmask: Ipv4Addr::new(255,255,255,0),
                        broadcast: Some(Ipv4Addr::new(192,160,0,255))
                    })
                }
            ];
        }

        it "should default to eth0" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &None).unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }

        it "should retrieve address from eth0" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("eth0".to_owned()))
                .unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }

        it "should retrieve address from wlan0" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("wlan0".to_owned()))
                .unwrap();
            assert_eq!(ip_addr, "192.168.0.14");
        }

        it "should retrieve address from docker0" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("docker0".to_owned())).unwrap();
            assert_eq!(ip_addr, "172.18.1.42");
        }
    }

    describe! ipv6 {
        before_each {
            use super::super::get_if_addrs::*;
            use std::net::{ Ipv4Addr, Ipv6Addr };

            let interfaces: Vec<Interface> = vec![
                Interface {
                    name: "eth0".to_owned(),
                    addr: IfAddr::V6(Ifv6Addr {
                        ip: Ipv6Addr::new(0,0,0,0,0,0xffff,0xc0a8,0x4),
                        netmask: Ipv6Addr::new(0xffff,0xffff,0xffff,0xffff,
                                               0xffff,0xffff,0xffff,0xffff),
                        broadcast: None
                    })
                },
                Interface {
                    name: "eth0".to_owned(),
                    addr: IfAddr::V4(Ifv4Addr {
                        ip: Ipv4Addr::new(192,168,0,4),
                        netmask: Ipv4Addr::new(255,255,255,0),
                        broadcast: Some(Ipv4Addr::new(192,160,0,255))
                    })
                },
                Interface {
                    name: "eth1".to_owned(),
                    addr: IfAddr::V6(Ifv6Addr {
                        ip: Ipv6Addr::new(0,0,0,0,0,0xffff,0xc0a8,0x4),
                        netmask: Ipv6Addr::new(0xffff,0xffff,0xffff,0xffff,
                                               0xffff,0xffff,0xffff,0xffff),
                        broadcast: None
                    })
                }
            ];
        }

        it "should return default IPv4" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &None)
                .unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }

        it "should return IPv6" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("eth1".to_owned()))
                .unwrap();
            assert_eq!(ip_addr, "::ffff:192.168.0.4");
        }

        it "should return IPv4 if both are specified" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &Some("eth0".to_owned()))
                .unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }

        it "should return IPv4 and eth0 by default" {
            let ip_addr = registrar.get_ip_addr_from_ifaces(&interfaces, &None).unwrap();
            assert_eq!(ip_addr, "192.168.0.4");
        }
    }
}
<|endoftext|>


<commit_before>mod entities;
mod input;
mod updates;
mod util;

use chrono::{DateTime, Duration as OldDuration, Utc};
use log::{error, trace};
use rand_core::{OsRng, RngCore};
use serde_derive::Serialize;
use std::collections::HashMap;
use std::mem;

pub use self::entities::*;
pub use self::input::*;
pub use self::updates::*;
pub use self::util::*;

use crate::server::{ArenaId, ArenaPoint, Direction, Line, Player, PlayerId};

const ARENA_WIDTH: usize = 800;
const ARENA_HEIGHT: usize = 800;
const ARENA_MAX_PLAYERS: usize = 8;
const ARENA_START_TIMER_SECONDS: i64 = 5;
const LIGHTCYCLE_SPEED: isize = 120;

#[derive(Debug, Clone, Serialize)]
pub struct Arena {
    pub id: ArenaId,
    pub width: usize,
    pub height: usize,
    pub max_players: usize,

    pub started: Option<DateTime<Utc>>,

    pub players: HashMap<PlayerId, Player>,
    pub lightcycles: HashMap<PlayerId, Lightcycle>,
    pub trails: HashMap<PlayerId, Trail>,

    #[serde(skip)]
    pub updates: Vec<ArenaUpdate>,
    #[serde(skip)]
    updates_applied_so_far: usize,
}

impl Arena {
    pub fn add_player(&mut self, player: Player) {
        self.updates.push(ArenaUpdate::AddPlayer(player.id, player));
    }

    pub fn remove_player(&mut self, player_id: PlayerId) {
        self.updates.push(ArenaUpdate::RemovePlayer(player_id));
    }

    pub fn clear_updates(&mut self) {
        self.updates.clear();
        self.updates_applied_so_far = 0;
    }

    pub fn apply_updates(&mut self) -> &mut Self {
        let updates = mem::replace(&mut self.updates, Vec::new());
        for update in updates.iter().skip(self.updates_applied_so_far) {
            update.apply(self);
            self.updates_applied_so_far += 1;
        }
        mem::replace(&mut self.updates, updates);

        self
    }

    pub fn process_input(&mut self, player_id: PlayerId, input_event: ArenaInput) {
        input_event
            .process_into_updates(self, player_id)
            .drain(..)
            .for_each(|update| self.updates.push(update));
    }

    pub fn update(&mut self) {
        // apply process_input updates
        self.apply_updates();

        let started = match self.started {
            Some(started) => started,
            None => return,
        };

        let now = Utc::now();
        if now < started {
            return;
        }

        self.update_lightcycle_positions()
            .apply_updates()
            .calculate_lightcycle_collisions()
            .apply_updates()
            .update_trail_positions()
            .apply_updates();
    }

    //
    // update helpers
    //

    fn update_lightcycle_positions(&mut self) -> &mut Self {
        for (id, lightcycle) in self.lightcycles.iter() {
            if lightcycle.dead {
                continue;
            };

            self.updates
                .push(ArenaUpdate::UpdateLightcycleApplyVelocity(*id))
        }
        self
    }

    fn calculate_lightcycle_collisions(&mut self) -> &mut Self {
        'next_lightcycle: for (id, lightcycle) in self.lightcycles.iter() {
            if lightcycle.dead {
                continue 'next_lightcycle;
            };

            // test for trail collisions
            for trail in self.trails.values() {
                for line in trail.points.windows(2) {
                    let start = line[0];
                    let end = line[1];

                    if is_point_on_line_2d(lightcycle.position, Line(start, end)) {
                        self.updates
                            .push(ArenaUpdate::UpdateLightcycleApplyDeath(*id));
                        continue 'next_lightcycle;
                    }
                }
            }

            // test for arena bounds collisions
            if lightcycle.position.x < 0
                || lightcycle.position.y < 0
                || lightcycle.position.x > self.width as isize
                || lightcycle.position.y > self.height as isize
            {
                self.updates
                    .push(ArenaUpdate::UpdateLightcycleApplyDeath(*id));
                continue 'next_lightcycle;
            }

            // test for collisions with other lightcycles
            if self
                .lightcycles
                .iter()
                .filter(|(_, other_lightcycle)| !other_lightcycle.dead)
                .filter(|(other_id, _)| id != *other_id)
                .any(|(_, other_lightcycle)| lightcycle.position == other_lightcycle.position)
            {
                self.updates
                    .push(ArenaUpdate::UpdateLightcycleApplyDeath(*id));
                continue 'next_lightcycle;
            }
        }
        self
    }

    fn update_trail_positions(&mut self) -> &mut Self {
        for id in self.trails.keys() {
            let latest_point = match self.lightcycles.get(id) {
                Some(lightcycle) => {
                    if lightcycle.dead {
                        continue;
                    };

                    lightcycle.position
                }
                None => {
                    error!(
                        "Failed to update trail position: No lightcycle with id {}",
                        id
                    );
                    continue;
                }
            };

            self.updates
                .push(ArenaUpdate::UpdateTrailReplaceLatestPoint(
                    *id,
                    latest_point,
                ));
        }
        self
    }
}

impl Default for Arena {
    fn default() -> Self {
        Self {
            id: ArenaId::new_v4(),
            width: ARENA_WIDTH,
            height: ARENA_HEIGHT,
            max_players: ARENA_MAX_PLAYERS,

            started: None,

            players: Default::default(),
            lightcycles: Default::default(),
            trails: Default::default(),

            updates: Default::default(),
            updates_applied_so_far: 0,
        }
    }
}

/// ArenaOverview represents an overview of an Arena for the arena selection screen.
#[derive(Debug, Clone, Serialize)]
pub struct ArenaOverview {
    id: ArenaId,
    max_players: usize,
    started: Option<DateTime<Utc>>,
    players: HashMap<PlayerId, Player>,
}

impl From<&Arena> for ArenaOverview {
    fn from(arena: &Arena) -> Self {
        Self {
            id: arena.id,
            max_players: arena.max_players,
            started: arena.started,
            players: arena.players.clone(),
        }
    }
}
<commit_msg>fixed lightcycles speed<commit_after>mod entities;
mod input;
mod updates;
mod util;

use chrono::{DateTime, Duration as OldDuration, Utc};
use log::{error, trace};
use rand_core::{OsRng, RngCore};
use serde_derive::Serialize;
use std::collections::HashMap;
use std::mem;

pub use self::entities::*;
pub use self::input::*;
pub use self::updates::*;
pub use self::util::*;

use crate::server::{ArenaId, ArenaPoint, Direction, Line, Player, PlayerId};

const ARENA_WIDTH: usize = 800;
const ARENA_HEIGHT: usize = 800;
const ARENA_MAX_PLAYERS: usize = 8;
const ARENA_START_TIMER_SECONDS: i64 = 5;
const LIGHTCYCLE_SPEED: isize = 1;

#[derive(Debug, Clone, Serialize)]
pub struct Arena {
    pub id: ArenaId,
    pub width: usize,
    pub height: usize,
    pub max_players: usize,

    pub started: Option<DateTime<Utc>>,

    pub players: HashMap<PlayerId, Player>,
    pub lightcycles: HashMap<PlayerId, Lightcycle>,
    pub trails: HashMap<PlayerId, Trail>,

    #[serde(skip)]
    pub updates: Vec<ArenaUpdate>,
    #[serde(skip)]
    updates_applied_so_far: usize,
}

impl Arena {
    pub fn add_player(&mut self, player: Player) {
        self.updates.push(ArenaUpdate::AddPlayer(player.id, player));
    }

    pub fn remove_player(&mut self, player_id: PlayerId) {
        self.updates.push(ArenaUpdate::RemovePlayer(player_id));
    }

    pub fn clear_updates(&mut self) {
        self.updates.clear();
        self.updates_applied_so_far = 0;
    }

    pub fn apply_updates(&mut self) -> &mut Self {
        let updates = mem::replace(&mut self.updates, Vec::new());
        for update in updates.iter().skip(self.updates_applied_so_far) {
            update.apply(self);
            self.updates_applied_so_far += 1;
        }
        mem::replace(&mut self.updates, updates);

        self
    }

    pub fn process_input(&mut self, player_id: PlayerId, input_event: ArenaInput) {
        input_event
            .process_into_updates(self, player_id)
            .drain(..)
            .for_each(|update| self.updates.push(update));
    }

    pub fn update(&mut self) {
        // apply process_input updates
        self.apply_updates();

        let started = match self.started {
            Some(started) => started,
            None => return,
        };

        let now = Utc::now();
        if now < started {
            return;
        }

        self.update_lightcycle_positions()
            .apply_updates()
            .calculate_lightcycle_collisions()
            .apply_updates()
            .update_trail_positions()
            .apply_updates();
    }

    //
    // update helpers
    //

    fn update_lightcycle_positions(&mut self) -> &mut Self {
        for (id, lightcycle) in self.lightcycles.iter() {
            if lightcycle.dead {
                continue;
            };

            self.updates
                .push(ArenaUpdate::UpdateLightcycleApplyVelocity(*id))
        }
        self
    }

    fn calculate_lightcycle_collisions(&mut self) -> &mut Self {
        'next_lightcycle: for (id, lightcycle) in self.lightcycles.iter() {
            if lightcycle.dead {
                continue 'next_lightcycle;
            };

            // test for trail collisions
            for trail in self.trails.values() {
                for line in trail.points.windows(2) {
                    let start = line[0];
                    let end = line[1];

                    if is_point_on_line_2d(lightcycle.position, Line(start, end)) {
                        self.updates
                            .push(ArenaUpdate::UpdateLightcycleApplyDeath(*id));
                        continue 'next_lightcycle;
                    }
                }
            }

            // test for arena bounds collisions
            if lightcycle.position.x < 0
                || lightcycle.position.y < 0
                || lightcycle.position.x > self.width as isize
                || lightcycle.position.y > self.height as isize
            {
                self.updates
                    .push(ArenaUpdate::UpdateLightcycleApplyDeath(*id));
                continue 'next_lightcycle;
            }

            // test for collisions with other lightcycles
            if self
                .lightcycles
                .iter()
                .filter(|(_, other_lightcycle)| !other_lightcycle.dead)
                .filter(|(other_id, _)| id != *other_id)
                .any(|(_, other_lightcycle)| lightcycle.position == other_lightcycle.position)
            {
                self.updates
                    .push(ArenaUpdate::UpdateLightcycleApplyDeath(*id));
                continue 'next_lightcycle;
            }
        }
        self
    }

    fn update_trail_positions(&mut self) -> &mut Self {
        for id in self.trails.keys() {
            let latest_point = match self.lightcycles.get(id) {
                Some(lightcycle) => {
                    if lightcycle.dead {
                        continue;
                    };

                    lightcycle.position
                }
                None => {
                    error!(
                        "Failed to update trail position: No lightcycle with id {}",
                        id
                    );
                    continue;
                }
            };

            self.updates
                .push(ArenaUpdate::UpdateTrailReplaceLatestPoint(
                    *id,
                    latest_point,
                ));
        }
        self
    }
}

impl Default for Arena {
    fn default() -> Self {
        Self {
            id: ArenaId::new_v4(),
            width: ARENA_WIDTH,
            height: ARENA_HEIGHT,
            max_players: ARENA_MAX_PLAYERS,

            started: None,

            players: Default::default(),
            lightcycles: Default::default(),
            trails: Default::default(),

            updates: Default::default(),
            updates_applied_so_far: 0,
        }
    }
}

/// ArenaOverview represents an overview of an Arena for the arena selection screen.
#[derive(Debug, Clone, Serialize)]
pub struct ArenaOverview {
    id: ArenaId,
    max_players: usize,
    started: Option<DateTime<Utc>>,
    players: HashMap<PlayerId, Player>,
}

impl From<&Arena> for ArenaOverview {
    fn from(arena: &Arena) -> Self {
        Self {
            id: arena.id,
            max_players: arena.max_players,
            started: arena.started,
            players: arena.players.clone(),
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::fmt::{Display, Formatter, Error};
use std::string::String;
use std::vec::Vec;

use alloc_box::AllocBox;
use super::binding::UniqueBinding;
use super::js_var::{JsVar, JsKey, JsType, JsPtrEnum};

macro_rules! ptr_type_mismatch {
    ($tag:expr, $ptr:expr) => {
        panic!("Pointer type mismatch: expected {:?} but received {:?}", $tag, $ptr);
    }
}

#[derive(Clone, Debug)]
pub struct JsObjStruct {
    pub proto: JsProto,
    pub name: String,
    pub dict: HashMap<JsKey, JsVar>,
}

impl JsObjStruct {
    #[allow(unused_variables)]
    pub fn new(proto: JsProto, name: &str, kv_tuples: Vec<(JsKey, JsVar, Option<JsPtrEnum>)>,
               allocator: &mut AllocBox) -> JsObjStruct {
        let proto_vec : Vec<_> = match proto {
            Some(ref obj) => obj.clone().dict.into_iter().map(|(key, var)| {
                let ptr = allocator.find_id(&var.unique).map(|ptr| ptr.borrow().clone());
                (key, JsVar::new(var.t), ptr)
            }).collect(),
            None => Vec::new(),
        };

        JsObjStruct {
            proto: proto,
            name: String::from(name),
            dict: proto_vec.into_iter().chain(kv_tuples.into_iter()).map(|(k, v, ptr)| {
                match v.t {
                    JsType::JsPtr(ref tag) => match ptr {
                        Some(ptr) => {
                            if tag.eq_ptr_type(&ptr) {
                                allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
                            } else {
                                ptr_type_mismatch!(tag, ptr);
                            }
                        },
                        None => ptr_type_mismatch!(tag, None::<JsPtrEnum>),
                    },
                    _ => match ptr {
                        Some(ptr) => ptr_type_mismatch!(None::<JsPtrEnum>, ptr),
                        None => {},
                    },
                }
                (k, v)
            }).collect()
        }
    }

    pub fn add_key(&mut self, obj_binding: &UniqueBinding, k: JsKey, v: JsVar, ptr: Option<JsPtrEnum>, allocator: &mut AllocBox) {
        if let Some(var) = self.dict.get(&k) {
            match var.t {
                JsType::JsPtr(_) => { allocator.condemn(var.unique.clone()).expect("Unable to whiten!") },
                _ => {}
            }
        }
        if let Some(ptr) = ptr {
            allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
        }

        match allocator.find_id(obj_binding) {
            Some(ref ptr) => match &mut *(ptr.borrow_mut()) {
                &mut JsPtrEnum::JsObj(ref mut obj) => obj.dict.insert(k.clone(), v.clone()),
                _ => panic!("Binding does not belong to an object!"),
            },
            None => panic!("No pointer with matching binding found!"),
        };

        self.dict.insert(k, v);
    }

    pub fn remove_key(&mut self, obj_binding: &UniqueBinding, k: &JsKey, allocator: &mut AllocBox) -> Option<(JsVar, Option<JsPtrEnum>)>{
        let var_opt = match &mut *(allocator.find_id(obj_binding).expect("No pointer with matching binding found!").borrow_mut()) {
            &mut JsPtrEnum::JsObj(ref mut obj) => obj.dict.remove(k),
            _ => panic!("Binding does not belong to an object!"),
        };

        if let Some(var) = var_opt {
            let ptr = allocator.find_id(&var.unique).map(|s| s.borrow().clone());
            allocator.condemn(var.unique.clone()).expect("Unable to whiten!");
            let _ = self.dict.remove(k);
            Some((var, ptr))
        } else {
            None
        }
    }

    pub fn get_children(&self) -> HashSet<UniqueBinding> {
        let mut bindings = HashSet::new();
        for v in self.dict.values() {
            match v.t {
                JsType::JsPtr(_) => { bindings.insert(v.unique.clone()); },
                _ => (),
            }
        }
        bindings
    }
}

impl Display for JsObjStruct {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        try!(write!(fmt, "{{ "));

        for (i, (ref key, ref val)) in self.dict.iter().enumerate() {
            if i != 0 {
                try!(write!(fmt, ", "));
            }

            try!(write!(fmt, "{}: {}", key, val));
        }

        write!(fmt, " }}")
    }
}

pub type JsProto = Option<Box<JsObjStruct>>;

// TODO nice JS object creation macro
//macro_rules! js_obj {
//    ( $kt:ty : $ke:expr => $vt:ty : $ve:expr ),* {
//        {
//
//        }
//    };
//}
<commit_msg>fixed final issue with `remove_key`<commit_after>use std::collections::hash_map::HashMap;
use std::collections::hash_set::HashSet;
use std::fmt::{Display, Formatter, Error};
use std::string::String;
use std::vec::Vec;

use alloc_box::AllocBox;
use super::binding::UniqueBinding;
use super::js_var::{JsVar, JsKey, JsType, JsPtrEnum};

macro_rules! ptr_type_mismatch {
    ($tag:expr, $ptr:expr) => {
        panic!("Pointer type mismatch: expected {:?} but received {:?}", $tag, $ptr);
    }
}

#[derive(Clone, Debug)]
pub struct JsObjStruct {
    pub proto: JsProto,
    pub name: String,
    pub dict: HashMap<JsKey, JsVar>,
}

impl JsObjStruct {
    #[allow(unused_variables)]
    pub fn new(proto: JsProto, name: &str, kv_tuples: Vec<(JsKey, JsVar, Option<JsPtrEnum>)>,
               allocator: &mut AllocBox) -> JsObjStruct {
        let proto_vec : Vec<_> = match proto {
            Some(ref obj) => obj.clone().dict.into_iter().map(|(key, var)| {
                let ptr = allocator.find_id(&var.unique).map(|ptr| ptr.borrow().clone());
                (key, JsVar::new(var.t), ptr)
            }).collect(),
            None => Vec::new(),
        };

        JsObjStruct {
            proto: proto,
            name: String::from(name),
            dict: proto_vec.into_iter().chain(kv_tuples.into_iter()).map(|(k, v, ptr)| {
                match v.t {
                    JsType::JsPtr(ref tag) => match ptr {
                        Some(ptr) => {
                            if tag.eq_ptr_type(&ptr) {
                                allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
                            } else {
                                ptr_type_mismatch!(tag, ptr);
                            }
                        },
                        None => ptr_type_mismatch!(tag, None::<JsPtrEnum>),
                    },
                    _ => match ptr {
                        Some(ptr) => ptr_type_mismatch!(None::<JsPtrEnum>, ptr),
                        None => {},
                    },
                }
                (k, v)
            }).collect()
        }
    }

    pub fn add_key(&mut self, obj_binding: &UniqueBinding, k: JsKey, v: JsVar, ptr: Option<JsPtrEnum>, allocator: &mut AllocBox) {
        if let Some(var) = self.dict.get(&k) {
            match var.t {
                JsType::JsPtr(_) => { allocator.condemn(var.unique.clone()).expect("Unable to whiten!") },
                _ => {}
            }
        }
        if let Some(ptr) = ptr {
            allocator.alloc(v.unique.clone(), ptr).expect("Unable to allocate!"); // TODO better error handling
        }

        match allocator.find_id(obj_binding) {
            Some(ref ptr) => match &mut *(ptr.borrow_mut()) {
                &mut JsPtrEnum::JsObj(ref mut obj) => obj.dict.insert(k.clone(), v.clone()),
                _ => panic!("Binding does not belong to an object!"),
            },
            None => panic!("No pointer with matching binding found!"),
        };

        self.dict.insert(k, v);
    }

    pub fn remove_key(&mut self, obj_binding: &UniqueBinding, k: &JsKey, allocator: &mut AllocBox) -> Option<(JsVar, Option<JsPtrEnum>)>{
        let var_opt = match &mut *(allocator.find_id(obj_binding).expect("No pointer with matching binding found!").borrow_mut()) {
            &mut JsPtrEnum::JsObj(ref mut obj) => obj.dict.remove(k),
            _ => panic!("Binding does not belong to an object!"),
        };

        if let Some(var) = var_opt {
            let ptr = allocator.find_id(&var.unique).map(|s| s.borrow().clone());

            if ptr.is_some() {
                allocator.condemn(var.unique.clone()).expect("Unable to whiten!");
            }

            let _ = self.dict.remove(k);
            Some((var, ptr))
        } else {
            None
        }
    }

    pub fn get_children(&self) -> HashSet<UniqueBinding> {
        let mut bindings = HashSet::new();
        for v in self.dict.values() {
            match v.t {
                JsType::JsPtr(_) => { bindings.insert(v.unique.clone()); },
                _ => (),
            }
        }
        bindings
    }
}

impl Display for JsObjStruct {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        try!(write!(fmt, "{{ "));

        for (i, (ref key, ref val)) in self.dict.iter().enumerate() {
            if i != 0 {
                try!(write!(fmt, ", "));
            }

            try!(write!(fmt, "{}: {}", key, val));
        }

        write!(fmt, " }}")
    }
}

pub type JsProto = Option<Box<JsObjStruct>>;

// TODO nice JS object creation macro
//macro_rules! js_obj {
//    ( $kt:ty : $ke:expr => $vt:ty : $ve:expr ),* {
//        {
//
//        }
//    };
//}
<|endoftext|>