<commit_before>use super::{super::heap::Pointer, Event, FiberId, FullTracer, InFiberEvent};
use crate::{
    compiler::{
        ast_to_hir::AstToHir,
        cst::{Cst, CstDb, CstKind},
        hir::Id,
    },
    database::Database,
    language_server::utils::LspPositionConversion,
    module::Module,
};
use itertools::Itertools;
use std::collections::HashMap;

// Stack traces are a reduced view of the tracing state that represent the stack
// trace at a given moment in time.

#[derive(Clone)]
pub enum StackEntry {
    Call {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    Needs {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    Module {
        module: Module,
    },
}

impl FullTracer {
    pub fn stack_traces(&self) -> HashMap<FiberId, Vec<StackEntry>> {
        let mut stacks: HashMap<FiberId, Vec<StackEntry>> = HashMap::new();
        for timed_event in &self.events {
            let Event::InFiber { fiber, event } = &timed_event.event else { continue; };
                let stack = stacks.entry(*fiber).or_default();
                match event {
                    InFiberEvent::ModuleStarted { module } => {
                        stack.push(StackEntry::Module {
                            module: module.clone(),
                        });
                    }
                    InFiberEvent::ModuleEnded { .. } => {
                        assert!(matches!(stack.pop().unwrap(), StackEntry::Module { .. }));
                    }
                    InFiberEvent::CallStarted { id, closure, args } => {
                        stack.push(StackEntry::Call {
                            id: id.clone(),
                            closure: *closure,
                            args: args.clone(),
                        });
                    }
                    InFiberEvent::CallEnded { .. } => {
                        assert!(matches!(stack.pop().unwrap(), StackEntry::Call { .. }));
                    }
                    InFiberEvent::NeedsStarted {
                        id,
                        condition,
                        reason,
                    } => {
                        stack.push(StackEntry::Needs {
                            id: id.clone(),
                            condition: *condition,
                            reason: *reason,
                        });
                    }
                    InFiberEvent::NeedsEnded => {
                        assert!(matches!(stack.pop().unwrap(), StackEntry::Needs { .. }));
                    }
                    _ => {}
                }
            }
        }
        stacks
    }
    pub fn format_stack_trace(&self, db: &Database, stack: &[StackEntry]) -> String {
        let mut lines = vec![];

        for entry in stack.iter().rev() {
            let hir_id = match entry {
                StackEntry::Call { id, .. } => Some(id),
                StackEntry::Needs { id, .. } => Some(id),
                StackEntry::Module { .. } => None,
            };
            let (cst_id, span) = if let Some(hir_id) = hir_id {
                let module = hir_id.module.clone();
                let cst_id = db.hir_to_cst_id(hir_id.clone());
                let cst = cst_id.map(|id| db.find_cst(module.clone(), id));
                let span = cst.map(|cst| {
                    (
                        db.offset_to_lsp(module.clone(), cst.span.start),
                        db.offset_to_lsp(module.clone(), cst.span.end),
                    )
                });
                (cst_id, span)
            } else {
                (None, None)
            };
            let caller_location_string = format!(
                "{} {}",
                hir_id
                    .map(|id| format!("{id}"))
                    .unwrap_or_else(|| "<no hir>".to_string()),
                span.map(|((start_line, start_col), (end_line, end_col))| format!(
                    "{}:{} – {}:{}",
                    start_line, start_col, end_line, end_col
                ))
                .unwrap_or_else(|| "<no location>".to_string())
            );
            let call_string = match entry {
                StackEntry::Call { closure, args, .. } => format!(
                    "{} {}",
                    cst_id
                        .and_then(|id| {
                            let cst = db.find_cst(hir_id.unwrap().module.clone(), id);
                            match cst.kind {
                                CstKind::Call { receiver, .. } => receiver.extract_receiver_name(),
                                _ => None,
                            }
                        })
                        .unwrap_or_else(|| closure.format(&self.heap)),
                    args.iter().map(|arg| arg.format(&self.heap)).join(" ")
                ),
                StackEntry::Needs {
                    condition, reason, ..
                } => format!(
                    "needs {} {}",
                    condition.format(&self.heap),
                    reason.format(&self.heap),
                ),
                StackEntry::Module { module } => format!("module {module}"),
            };
            lines.push(format!("{caller_location_string:90} {call_string}"));
        }
        lines.join("\n")
    }
    /// When a VM panics, some child fiber might be responsible for that. This
    /// function returns a formatted stack trace spanning all fibers in the
    /// chain from the panicking root fiber until the concrete failing needs.
    pub fn format_panic_stack_trace_to_root_fiber(&self, db: &Database) -> String {
        let mut panicking_fiber_chain = vec![FiberId::root()];
        for timed_event in self.events.iter().rev() {
            if let Event::FiberPanicked {
                fiber,
                panicked_child,
            } = timed_event.event
            {
                if fiber == *panicking_fiber_chain.last().unwrap() {
                    match panicked_child {
                        Some(child) => panicking_fiber_chain.push(child),
                        None => break,
                    }
                }
            }
        }

        let stack_traces = self.stack_traces();
        panicking_fiber_chain
            .into_iter()
            .rev()
            .map(|fiber| self.format_stack_trace(db, &stack_traces[&fiber]))
            .join("\n(fiber boundary)\n")
    }
}

impl Cst {
    fn extract_receiver_name(&self) -> Option<String> {
        match &self.kind {
            CstKind::TrailingWhitespace { child, .. } => child.extract_receiver_name(),
            CstKind::Identifier(identifier) => Some(identifier.to_string()),
            CstKind::Parenthesized { inner, .. } => inner.extract_receiver_name(),
            CstKind::StructAccess { struct_, key, .. } => {
                let struct_string = struct_.extract_receiver_name()?;
                let key = key.extract_receiver_name()?;
                Some(format!("{struct_string}.{key}"))
            }
            _ => None,
        }
    }
}
<commit_msg>Fix indentation<commit_after>use super::{super::heap::Pointer, Event, FiberId, FullTracer, InFiberEvent};
use crate::{
    compiler::{
        ast_to_hir::AstToHir,
        cst::{Cst, CstDb, CstKind},
        hir::Id,
    },
    database::Database,
    language_server::utils::LspPositionConversion,
    module::Module,
};
use itertools::Itertools;
use std::collections::HashMap;

// Stack traces are a reduced view of the tracing state that represent the stack
// trace at a given moment in time.

#[derive(Clone)]
pub enum StackEntry {
    Call {
        id: Id,
        closure: Pointer,
        args: Vec<Pointer>,
    },
    Needs {
        id: Id,
        condition: Pointer,
        reason: Pointer,
    },
    Module {
        module: Module,
    },
}

impl FullTracer {
    pub fn stack_traces(&self) -> HashMap<FiberId, Vec<StackEntry>> {
        let mut stacks: HashMap<FiberId, Vec<StackEntry>> = HashMap::new();
        for timed_event in &self.events {
            let Event::InFiber { fiber, event } = &timed_event.event else { continue; };
            let stack = stacks.entry(*fiber).or_default();
            match event {
                InFiberEvent::ModuleStarted { module } => {
                    stack.push(StackEntry::Module {
                        module: module.clone(),
                    });
                }
                InFiberEvent::ModuleEnded { .. } => {
                    assert!(matches!(stack.pop().unwrap(), StackEntry::Module { .. }));
                }
                InFiberEvent::CallStarted { id, closure, args } => {
                    stack.push(StackEntry::Call {
                        id: id.clone(),
                        closure: *closure,
                        args: args.clone(),
                    });
                }
                InFiberEvent::CallEnded { .. } => {
                    assert!(matches!(stack.pop().unwrap(), StackEntry::Call { .. }));
                }
                InFiberEvent::NeedsStarted {
                    id,
                    condition,
                    reason,
                } => {
                    stack.push(StackEntry::Needs {
                        id: id.clone(),
                        condition: *condition,
                        reason: *reason,
                    });
                }
                InFiberEvent::NeedsEnded => {
                    assert!(matches!(stack.pop().unwrap(), StackEntry::Needs { .. }));
                }
                _ => {}
            }
        }
        stacks
    }
    pub fn format_stack_trace(&self, db: &Database, stack: &[StackEntry]) -> String {
        let mut lines = vec![];

        for entry in stack.iter().rev() {
            let hir_id = match entry {
                StackEntry::Call { id, .. } => Some(id),
                StackEntry::Needs { id, .. } => Some(id),
                StackEntry::Module { .. } => None,
            };
            let (cst_id, span) = if let Some(hir_id) = hir_id {
                let module = hir_id.module.clone();
                let cst_id = db.hir_to_cst_id(hir_id.clone());
                let cst = cst_id.map(|id| db.find_cst(module.clone(), id));
                let span = cst.map(|cst| {
                    (
                        db.offset_to_lsp(module.clone(), cst.span.start),
                        db.offset_to_lsp(module.clone(), cst.span.end),
                    )
                });
                (cst_id, span)
            } else {
                (None, None)
            };
            let caller_location_string = format!(
                "{} {}",
                hir_id
                    .map(|id| format!("{id}"))
                    .unwrap_or_else(|| "<no hir>".to_string()),
                span.map(|((start_line, start_col), (end_line, end_col))| format!(
                    "{}:{} – {}:{}",
                    start_line, start_col, end_line, end_col
                ))
                .unwrap_or_else(|| "<no location>".to_string())
            );
            let call_string = match entry {
                StackEntry::Call { closure, args, .. } => format!(
                    "{} {}",
                    cst_id
                        .and_then(|id| {
                            let cst = db.find_cst(hir_id.unwrap().module.clone(), id);
                            match cst.kind {
                                CstKind::Call { receiver, .. } => receiver.extract_receiver_name(),
                                _ => None,
                            }
                        })
                        .unwrap_or_else(|| closure.format(&self.heap)),
                    args.iter().map(|arg| arg.format(&self.heap)).join(" ")
                ),
                StackEntry::Needs {
                    condition, reason, ..
                } => format!(
                    "needs {} {}",
                    condition.format(&self.heap),
                    reason.format(&self.heap),
                ),
                StackEntry::Module { module } => format!("module {module}"),
            };
            lines.push(format!("{caller_location_string:90} {call_string}"));
        }
        lines.join("\n")
    }
    /// When a VM panics, some child fiber might be responsible for that. This
    /// function returns a formatted stack trace spanning all fibers in the
    /// chain from the panicking root fiber until the concrete failing needs.
    pub fn format_panic_stack_trace_to_root_fiber(&self, db: &Database) -> String {
        let mut panicking_fiber_chain = vec![FiberId::root()];
        for timed_event in self.events.iter().rev() {
            if let Event::FiberPanicked {
                fiber,
                panicked_child,
            } = timed_event.event
            {
                if fiber == *panicking_fiber_chain.last().unwrap() {
                    match panicked_child {
                        Some(child) => panicking_fiber_chain.push(child),
                        None => break,
                    }
                }
            }
        }

        let stack_traces = self.stack_traces();
        panicking_fiber_chain
            .into_iter()
            .rev()
            .map(|fiber| self.format_stack_trace(db, &stack_traces[&fiber]))
            .join("\n(fiber boundary)\n")
    }
}

impl Cst {
    fn extract_receiver_name(&self) -> Option<String> {
        match &self.kind {
            CstKind::TrailingWhitespace { child, .. } => child.extract_receiver_name(),
            CstKind::Identifier(identifier) => Some(identifier.to_string()),
            CstKind::Parenthesized { inner, .. } => inner.extract_receiver_name(),
            CstKind::StructAccess { struct_, key, .. } => {
                let struct_string = struct_.extract_receiver_name()?;
                let key = key.extract_receiver_name()?;
                Some(format!("{struct_string}.{key}"))
            }
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;

use serde_json::value::Value;

use priority::TaskPriority;

pub type DateTime= String; // FIXME
pub type Project = String;
pub type Status  = String;
pub type Tag     = String;
pub type UUID    = String; // FIXME
pub type Urgency = f64;

#[derive(Debug, Clone)]
pub struct Task {
    id: u64,
    desc: String,
    entry: DateTime,
    modified: DateTime,
    priority: TaskPriority,
    project: Project,
    status: Status,
    tags: Vec<Tag>,
    uuid: UUID,
    urgency: Urgency,
}

/*
 * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice
 * to us. I guess this should be fixed.
 */
impl Task {

    pub fn new(id: u64, desc: String, entry: DateTime, modified: DateTime,
        priority: TaskPriority, project: Project, status: Status,
        tags: Vec<Tag>, uuid: UUID, urgency: Urgency)
        -> Task
    {
        Task {
            id       : id,
            desc     : desc,
            entry    : entry,
            modified : modified,
            priority : priority,
            project  : project,
            status   : status,
            tags     : tags,
            uuid     : uuid,
            urgency  : urgency,
        }
    }

    pub fn from_value(v: Value) -> Option<Task> {
        if !v.is_object() {
            return None;
        }

        let map = v.as_object().unwrap();

        let keys = [ "id", "description", "modified", "priority", "project",
            "status", "tags", "uuid", "urgency"];

        if keys.iter().any(|x| map.contains_key(*x)) {
            return None
        }

        Some(Task {
            id       : get_id(&map),
            desc     : get_desc(&map),
            entry    : get_entry(&map),
            modified : get_modified(&map),
            priority : TaskPriority::from(get_priority(&map)),
            project  : get_project(&map),
            status   : get_status(&map),
            tags     : get_tags(&map),
            uuid     : get_uuid(&map),
            urgency  : get_urgency(&map),
        })
    }

    pub fn id(&self) -> u64 {
        self.id
    }

    pub fn desc(&self) -> &String {
        &self.desc
    }

    pub fn entry(&self) -> &DateTime {
        &self.entry
    }

    pub fn modified(&self) -> &DateTime {
        &self.modified
    }

    pub fn priority(&self) -> TaskPriority {
        self.priority.clone()
    }

    pub fn project(&self) -> &Project {
        &self.project
    }

    pub fn status(&self) -> &Status {
        &self.status
    }

    pub fn tags(&self) -> &Vec<Tag> {
        &self.tags
    }

    pub fn uuid(&self) -> &UUID {
        &self.uuid
    }

    pub fn urgency(&self) -> Urgency {
        self.urgency
    }

}

impl Into<Value> for Task {

    fn into(self) -> Value {
        let id       = Value::U64(self.id);
        let desc     = Value::String(self.desc);
        let entry    = Value::String(self.entry);
        let modified = Value::String(self.modified);
        let priority = Value::String(self.priority.into());
        let project  = Value::String(self.project);
        let status   = Value::String(self.status);
        let tags     = Value::Array(self.tags
                                        .into_iter()
                                        .map(|s| Value::String(String::from(s)))
                                        .collect());
        let uuid     = Value::String(self.uuid);
        let urgency  = Value::F64(self.urgency);

        let mut map = BTreeMap::new();
        map.insert(String::from("id")       , id);
        map.insert(String::from("desc")     , desc);
        map.insert(String::from("entry")    , entry);
        map.insert(String::from("modified") , modified);
        map.insert(String::from("priority") , priority);
        map.insert(String::from("project")  , project);
        map.insert(String::from("status")   , status);
        map.insert(String::from("tags")     , tags);
        map.insert(String::from("uuid")     , uuid);
        map.insert(String::from("urgency")  , urgency);

        Value::Object(map)
    }

}

fn get_id(map: &BTreeMap<String, Value>) -> u64 {
    map.get("id").unwrap().as_u64().unwrap()
}

fn get_desc(map: &BTreeMap<String, Value>) -> String {
    map.get("description").unwrap().as_string().map(String::from).unwrap()
}

fn get_entry(map: &BTreeMap<String, Value>) -> String {
    map.get("entry").unwrap().as_string().map(String::from).unwrap()
}

fn get_modified(map: &BTreeMap<String, Value>) -> String {
    map.get("modified").unwrap().as_string().map(String::from).unwrap()
}

fn get_priority(map: &BTreeMap<String, Value>) -> &str {
    map.get("priority").unwrap().as_string().unwrap()
}

fn get_project(map: &BTreeMap<String, Value>) -> String {
    map.get("project").unwrap().as_string().map(String::from).unwrap()
}

fn get_status(map: &BTreeMap<String, Value>) -> String {
    map.get("status").unwrap().as_string().map(String::from).unwrap()
}

fn get_tags(map: &BTreeMap<String, Value>) -> Vec<Tag> {
    map.get("tags")
        .unwrap()
        .as_array()
        .unwrap()
        .clone()
        .into_iter()
        .map(|item| item.as_string().map(String::from).unwrap())
        .collect()
}

fn get_uuid(map: &BTreeMap<String, Value>) -> String {
    map.get("uuid").unwrap().as_string().map(String::from).unwrap()
}

fn get_urgency(map: &BTreeMap<String, Value>) -> f64 {
    map.get("urgency").unwrap().as_f64().unwrap()
}

<commit_msg>Add test<commit_after>use std::collections::BTreeMap;

use serde_json::value::Value;

use priority::TaskPriority;

pub type DateTime= String; // FIXME
pub type Project = String;
pub type Status  = String;
pub type Tag     = String;
pub type UUID    = String; // FIXME
pub type Urgency = f64;

#[derive(Debug, Clone)]
pub struct Task {
    id: u64,
    desc: String,
    entry: DateTime,
    modified: DateTime,
    priority: TaskPriority,
    project: Project,
    status: Status,
    tags: Vec<Tag>,
    uuid: UUID,
    urgency: Urgency,
}

/*
 * TODO: We do not fail if the JSON parsing fails. This panics. We rely on taskwarrior to be nice
 * to us. I guess this should be fixed.
 */
impl Task {

    pub fn new(id: u64, desc: String, entry: DateTime, modified: DateTime,
        priority: TaskPriority, project: Project, status: Status,
        tags: Vec<Tag>, uuid: UUID, urgency: Urgency)
        -> Task
    {
        Task {
            id       : id,
            desc     : desc,
            entry    : entry,
            modified : modified,
            priority : priority,
            project  : project,
            status   : status,
            tags     : tags,
            uuid     : uuid,
            urgency  : urgency,
        }
    }

    pub fn from_value(v: Value) -> Option<Task> {
        if !v.is_object() {
            return None;
        }

        let map = v.as_object().unwrap();

        let keys = [ "id", "description", "modified", "priority", "project",
            "status", "tags", "uuid", "urgency"];

        if keys.iter().any(|x| map.contains_key(*x)) {
            return None
        }

        Some(Task {
            id       : get_id(&map),
            desc     : get_desc(&map),
            entry    : get_entry(&map),
            modified : get_modified(&map),
            priority : TaskPriority::from(get_priority(&map)),
            project  : get_project(&map),
            status   : get_status(&map),
            tags     : get_tags(&map),
            uuid     : get_uuid(&map),
            urgency  : get_urgency(&map),
        })
    }

    pub fn id(&self) -> u64 {
        self.id
    }

    pub fn desc(&self) -> &String {
        &self.desc
    }

    pub fn entry(&self) -> &DateTime {
        &self.entry
    }

    pub fn modified(&self) -> &DateTime {
        &self.modified
    }

    pub fn priority(&self) -> TaskPriority {
        self.priority.clone()
    }

    pub fn project(&self) -> &Project {
        &self.project
    }

    pub fn status(&self) -> &Status {
        &self.status
    }

    pub fn tags(&self) -> &Vec<Tag> {
        &self.tags
    }

    pub fn uuid(&self) -> &UUID {
        &self.uuid
    }

    pub fn urgency(&self) -> Urgency {
        self.urgency
    }

}

impl Into<Value> for Task {

    fn into(self) -> Value {
        let id       = Value::U64(self.id);
        let desc     = Value::String(self.desc);
        let entry    = Value::String(self.entry);
        let modified = Value::String(self.modified);
        let priority = Value::String(self.priority.into());
        let project  = Value::String(self.project);
        let status   = Value::String(self.status);
        let tags     = Value::Array(self.tags
                                        .into_iter()
                                        .map(|s| Value::String(String::from(s)))
                                        .collect());
        let uuid     = Value::String(self.uuid);
        let urgency  = Value::F64(self.urgency);

        let mut map = BTreeMap::new();
        map.insert(String::from("id")       , id);
        map.insert(String::from("desc")     , desc);
        map.insert(String::from("entry")    , entry);
        map.insert(String::from("modified") , modified);
        map.insert(String::from("priority") , priority);
        map.insert(String::from("project")  , project);
        map.insert(String::from("status")   , status);
        map.insert(String::from("tags")     , tags);
        map.insert(String::from("uuid")     , uuid);
        map.insert(String::from("urgency")  , urgency);

        Value::Object(map)
    }

}

fn get_id(map: &BTreeMap<String, Value>) -> u64 {
    map.get("id").unwrap().as_u64().unwrap()
}

fn get_desc(map: &BTreeMap<String, Value>) -> String {
    map.get("description").unwrap().as_string().map(String::from).unwrap()
}

fn get_entry(map: &BTreeMap<String, Value>) -> String {
    map.get("entry").unwrap().as_string().map(String::from).unwrap()
}

fn get_modified(map: &BTreeMap<String, Value>) -> String {
    map.get("modified").unwrap().as_string().map(String::from).unwrap()
}

fn get_priority(map: &BTreeMap<String, Value>) -> &str {
    map.get("priority").unwrap().as_string().unwrap()
}

fn get_project(map: &BTreeMap<String, Value>) -> String {
    map.get("project").unwrap().as_string().map(String::from).unwrap()
}

fn get_status(map: &BTreeMap<String, Value>) -> String {
    map.get("status").unwrap().as_string().map(String::from).unwrap()
}

fn get_tags(map: &BTreeMap<String, Value>) -> Vec<Tag> {
    map.get("tags")
        .unwrap()
        .as_array()
        .unwrap()
        .clone()
        .into_iter()
        .map(|item| item.as_string().map(String::from).unwrap())
        .collect()
}

fn get_uuid(map: &BTreeMap<String, Value>) -> String {
    map.get("uuid").unwrap().as_string().map(String::from).unwrap()
}

fn get_urgency(map: &BTreeMap<String, Value>) -> f64 {
    map.get("urgency").unwrap().as_f64().unwrap()
}

#[cfg(test)]
mod test {
    use core::reader::Reader;
    use core::reader::JsonObjectReader;
    use super::Task;
    use priority::TaskPriority;
    use std::borrow::Borrow;

    #[test]
    fn test_from_json() {
        let json = String::from("{\"id\":1,\"description\":\"desc\",\"entry\":\"20150612T164806Z\",\"modified\":\"20160315T215656Z\",\"priority\":\"L\",\"project\":\"someproj\",\"status\":\"pending\",\"tags\":[\"test\",\"task\"],\"uuid\":\"93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3\",\"urgency\":1.0}");
        let bytes = json.into_bytes();
        let mut reader = JsonObjectReader::new(Reader::new(bytes.borrow()));

        let v = reader.next();
        assert!(v.is_some());
        let v = v.unwrap();

        let t = Task::from_value(v);
        assert!(t.is_some());
        let t = t.unwrap();

        assert_eq!(t.id(), 1);
        assert_eq!(t.desc().clone(), String::from("desc"));
        assert_eq!(t.entry().clone(), String::from("20150612T164806Z"));
        assert_eq!(t.modified().clone(), String::from("20160315T215656Z"));
        assert_eq!(t.priority(), TaskPriority::Low);
        assert_eq!(t.project().clone(), String::from("someproj"));
        assert_eq!(t.status().clone(), String::from("pending"));
        for n in ["test", "task"].iter() {
            assert!(t.tags().contains(&String::from(*n)));
        }
        assert_eq!(t.uuid().clone(), String::from("93cfc5fa-2f0c-44e6-bede-c2b1ca7ceff3"));
        assert_eq!(t.urgency() as u64, 1.0 as u64);
    }

}
<|endoftext|>


<commit_before>#![feature(conservative_impl_trait)]
extern crate time as time2;
extern crate crypto;
extern crate rustc_serialize as serialize;
#[macro_use]
extern crate url;
#[macro_use]
extern crate lazy_static;
extern crate hyper;

// use crypto::mac::Mac;
// use crypto::hmac::Hmac;
// use crypto::sha2::*;
//
// use serialize::base64::{self, ToBase64};
//
// use url::percent_encoding::{utf8_percent_encode, USERINFO_ENCODE_SET};
//
const CONNECTION_STRING: &'static str = "Endpoint=sb://personal.servicebus.windows.net/;\
                                         SharedAccessKeyName=RootManageSharedAccessKey;\
                                         SharedAccessKey=/ltI/wU/bT4cqc+V/C5KDwxG8rjHqU5IOS+aC2j4aQo=";
// const PRIMARY_KEY: &'static str = "/ltI/wU/bT4cqc+V/C5KDwxG8rjHqU5IOS+aC2j4aQo=";
// const QUEUE_URL: &'static str = "sb://personal.servicebus.windows.net/";
//

mod core;
mod servicebus;

pub fn main() {
    // let mut h = Hmac::new(Sha256::new(), PRIMARY_KEY.as_bytes());
    // let expiry = time2::now_utc() + time2::Duration::minutes(600);
    // println!("{}\n", expiry.to_timespec().sec);
    //
    // let encoded_url = utf8_percent_encode(QUEUE_URL, USERINFO_ENCODE_SET).collect::<String>();
    // println!("{:?}\n", encoded_url);
    //
    // let message = format!("{}\n{}", encoded_url, expiry.to_timespec().sec);
    // println!("{}\n", message);
    //
    // h.input(message.as_bytes());
    // let mut sig = h.result().code().to_base64(base64::STANDARD);
    // sig = utf8_percent_encode(&sig, USERINFO_ENCODE_SET).collect::<String>();
    // println!("{:?}\n", sig);
    //
    // let sas = format!("SharedAccessSignature sig={}&se={}&skn={}&sr={}",
    // sig,
    // expiry.to_timespec().sec,
    // "RootManageSharedAccessKey",
    // encoded_url);
    //

    let sas2 = core::generate_sas(CONNECTION_STRING, std::time::Duration::from_secs(600));
    println!("\n\n");
    println!("{}", sas2);
}<commit_msg>Delete temp.rs<commit_after><|endoftext|>


<commit_before>use yoga_wrapper;

use renderable::Renderable;
use style::{BackgroundColor, Style};

#[derive(Debug, Default)]
pub struct Text<'text, C> {
    text: &'text str,
    style: Style<C>,
}

impl<'text, C> Text<'text, C> {
    pub fn new(text: &'text str) -> Text<'text, C>
        where C: Default
    {
        Text {
            text: text,
            style: Style::new(),
        }
    }
}

impl<'text, C> Renderable<C> for Text<'text, C> {
    fn get_node(&self) -> &yoga_wrapper::Node {
        self.style.get_node()
    }

    fn get_style(&self) -> &Style<C> {
        &self.style
    }

    fn get_mut_style(&mut self) -> &mut Style<C> {
        &mut self.style
    }

    fn get_child(&self, _: usize) -> Option<&Renderable<C>> {
        None
    }

    fn get_child_count(&self) -> usize {
        0
    }
}

#[cfg(test)]
mod tests {
    use yoga_wrapper;

    use Text;
    use View;
    use Backend;
    use Builds;
    use Renders;
    use Renderable;

    struct Builder {
        measurer: Measurer,
    }

    impl<'meas> Builds<'meas, i32> for Builder {
        fn create_context<'text>(&'meas self,
                                 text: &'text str)
                                 -> yoga_wrapper::Context<'text, 'meas> {
            yoga_wrapper::Context::new(text, &self.measurer)
        }

        fn view<'r>(&self) -> View<'r, i32> {
            View::new()
        }

        fn text<'t, 'a: 't>(&'a self, text: &'t str) -> Text<'t, i32> {
            Text::new(text)
        }
    }

    struct Renderer {}

    impl<'meas> Renders<'meas> for Renderer {
        type Color = i32;
        type Builder = Builder;

        fn render(&mut self, node: &Renderable<i32>) {}
    }

    struct Measurer {}

    impl yoga_wrapper::Measures for Measurer {
        fn measure(&self, text: &str) -> yoga_wrapper::Size {
            yoga_wrapper::Size {
                width: text.len() as f32,
                height: 1.0,
            }
        }
    }

    struct TestBackend {
        renderer: Renderer,
        measurer: Measurer,
    }

    impl TestBackend {
        pub fn new() -> TestBackend {
            TestBackend {
                renderer: Renderer {},
                measurer: Measurer {},
            }
        }
    }

    impl<'meas> Backend<'meas> for TestBackend {
        type Renderer = Renderer;

        fn get_renderer(&mut self) -> &mut Self::Renderer {
            &mut self.renderer
        }
    }

    #[test]
    fn it_works() {
        let be = TestBackend::new();
        let _: Text<i32> = Text::new("yo!");
    }
}
<commit_msg>chore(Text): change new sig to take context<commit_after>use yoga_wrapper;

use renderable::Renderable;
use style::{BackgroundColor, Style};

#[derive(Debug, Default)]
pub struct Text<'text, C> {
    text: &'text str,
    style: Style<C>,
}

impl<'text, C> Text<'text, C> {
    pub fn new(text: &'text str, context: &mut yoga_wrapper::Context) -> Text<'text, C>
        where C: Default
    {
        let mut style = Style::new();

        {
            let node = style.get_mut_node();
            node.set_measure_func(yoga_wrapper::measure);
            node.set_context(context);
        }

        Text {
            text: text,
            style: style,
        }
    }
}

impl<'text, C> Renderable<C> for Text<'text, C> {
    fn get_node(&self) -> &yoga_wrapper::Node {
        self.style.get_node()
    }

    fn get_style(&self) -> &Style<C> {
        &self.style
    }

    fn get_mut_style(&mut self) -> &mut Style<C> {
        &mut self.style
    }

    fn get_child(&self, _: usize) -> Option<&Renderable<C>> {
        None
    }

    fn get_child_count(&self) -> usize {
        0
    }
}

#[cfg(test)]
mod tests {
    use yoga_wrapper;

    use Text;
    use View;
    use Backend;
    use Builds;
    use Renders;
    use Renderable;

    struct Builder {
        measurer: Measurer,
    }

    impl<'meas> Builds<'meas, i32> for Builder {
        fn create_context<'text>(&'meas self,
                                 text: &'text str)
                                 -> yoga_wrapper::Context<'text, 'meas> {
            yoga_wrapper::Context::new(text, &self.measurer)
        }

        fn view<'r>(&self) -> View<'r, i32> {
            View::new()
        }

        fn text<'t, 'a: 't>(&'a self, text: &'t str) -> Text<'t, i32> {
            Text::new(text)
        }
    }

    struct Renderer {}

    impl<'meas> Renders<'meas> for Renderer {
        type Color = i32;
        type Builder = Builder;

        fn render(&mut self, node: &Renderable<i32>) {}
    }

    struct Measurer {}

    impl yoga_wrapper::Measures for Measurer {
        fn measure(&self, text: &str) -> yoga_wrapper::Size {
            yoga_wrapper::Size {
                width: text.len() as f32,
                height: 1.0,
            }
        }
    }

    struct TestBackend {
        renderer: Renderer,
        measurer: Measurer,
    }

    impl TestBackend {
        pub fn new() -> TestBackend {
            TestBackend {
                renderer: Renderer {},
                measurer: Measurer {},
            }
        }
    }

    impl<'meas> Backend<'meas> for TestBackend {
        type Renderer = Renderer;

        fn get_renderer(&mut self) -> &mut Self::Renderer {
            &mut self.renderer
        }
    }

    #[test]
    fn it_works() {
        let be = TestBackend::new();
        let _: Text<i32> = Text::new("yo!");
    }
}
<|endoftext|>


<commit_before>//! A trait for objects that process in discrete time steps.

use std::sync::mpsc::channel;
use std::thread;

use types::{SAMPLE_RATE, Time};

/// Methods for processing something in discrete time steps. By defining how to
/// perform a single tick, this trait gives several convenience methods.
///
/// A single tick could correspond to one frame, but it doesn't have to. For
/// example, if an object does buffering it may process many samples per tick.
pub trait Tick {
    /// Handles one time step.
    fn tick(&mut self);

    /// Runs `tick` `n` times. Returns after processing.
    fn tick_n_times(&mut self, n: Time) {
        for _ in 0..n {
            self.tick();
        }
    }

    /// Ticks into infinity. Never returns.
    fn tick_forever(&mut self) -> ! {
        loop {
            self.tick();
        }
    }

    /// Ticks while waiting for the user to press `Enter`. When enter is
    /// pressed, ticking stops and the method returns.
    fn tick_until_enter(&mut self) {
        let (tx, rx) = channel();
        let _ = thread::spawn(move || {
            use std::io::{Read, stdin};
            let mut buf = [0];
            let _ = stdin().read(&mut buf);
            tx.send(()).expect("Failed to send end message.");
        });

        let ticks = SAMPLE_RATE / 10;
        loop {
            // Tick for 100ms, then check for exit command
            for _ in 0..ticks {
                self.tick();
            }
            if rx.try_recv().is_ok() {
                break;
            }
        }
    }
}
<commit_msg>Update tick until enter to use atomics<commit_after>//! A trait for objects that process in discrete time steps.

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

use types::{SAMPLE_RATE, Time};

/// Methods for processing something in discrete time steps. By defining how to
/// perform a single tick, this trait gives several convenience methods.
///
/// A single tick could correspond to one frame, but it doesn't have to. For
/// example, if an object does buffering it may process many samples per tick.
pub trait Tick {
    /// Handles one time step.
    fn tick(&mut self);

    /// Runs `tick` `n` times. Returns after processing.
    fn tick_n_times(&mut self, n: Time) {
        for _ in 0..n {
            self.tick();
        }
    }

    /// Ticks into infinity. Never returns.
    fn tick_forever(&mut self) -> ! {
        loop {
            self.tick();
        }
    }

    /// Ticks while waiting for the user to press `Enter`. When enter is
    /// pressed, ticking stops and the method returns.
    fn tick_until_enter(&mut self) {
        let stopped = Arc::new(AtomicBool::new(false));
        let signal_stop = stopped.clone();
        thread::spawn(move || {
            use std::io::{Read, stdin};
            let mut buf = [0];
            let _ = stdin().read(&mut buf);
            signal_stop.store(true, Ordering::Relaxed);
        });

        let ticks = SAMPLE_RATE / 10;
        loop {
            // Tick for 100ms, then check for exit command
            for _ in 0..ticks {
                self.tick();
            }
            if stopped.load(Ordering::Relaxed) {
                break;
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Decoding and Encoding of TIFF Images
//!
//! TIFF (Tagged Image File Format) is a versatile image format that supports
//! lossless and lossy compression.
//!
//! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification

extern crate tiff;

use std::io::{self, Cursor, Read, Write, Seek};
use std::marker::PhantomData;
use std::mem;

use color::ColorType;
use image::{ImageDecoder, ImageResult, ImageError};
use utils::vec_u16_into_u8;

/// Decoder for TIFF images.
pub struct TIFFDecoder<R>
    where R: Read + Seek
{
    dimensions: (u32, u32),
    colortype: ColorType,
    inner: tiff::decoder::Decoder<R>,
}

impl<R> TIFFDecoder<R>
    where R: Read + Seek
{
    /// Create a new TIFFDecoder.
    pub fn new(r: R) -> Result<TIFFDecoder<R>, ImageError> {
        let mut inner = tiff::decoder::Decoder::new(r)?;
        let dimensions = inner.dimensions()?;
        let colortype = inner.colortype()?.into();

        Ok(TIFFDecoder {
            dimensions,
            colortype,
            inner,
        })
    }
}

impl From<tiff::TiffError> for ImageError {
    fn from(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            tiff::TiffError::FormatError(desc) => ImageError::FormatError(desc.to_string()),
            tiff::TiffError::UnsupportedError(desc) => ImageError::UnsupportedError(desc.to_string()),
            tiff::TiffError::LimitsExceeded => ImageError::InsufficientMemory,
        }
    }
}

impl From<tiff::ColorType> for ColorType {
    fn from(ct: tiff::ColorType) -> ColorType {
        match ct {
            tiff::ColorType::Palette(depth) => ColorType::Unknown(depth),
            tiff::ColorType::Gray(1) => ColorType::L1,
            tiff::ColorType::Gray(8) => ColorType::L8,
            tiff::ColorType::Gray(16) => ColorType::L16,
            tiff::ColorType::GrayA(8) => ColorType::LA,
            tiff::ColorType::GrayA(16) => ColorType::LA16,
            tiff::ColorType::RGB(8) => ColorType::RGB,
            tiff::ColorType::RGBA(8) => ColorType::RGBA,
            tiff::ColorType::CMYK(_) => unimplemented!(),
            _ => unimplemented!(),
        }
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for TiffReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for TIFFDecoder<R> {
    type Reader = TiffReader<R>;

    fn dimensions(&self) -> (u64, u64) {
        (self.dimensions.0 as u64, self.dimensions.1 as u64)
    }

    fn colortype(&self) -> ColorType {
        self.colortype
    }

    fn into_reader(self) -> ImageResult<Self::Reader> {
        Ok(TiffReader(Cursor::new(self.read_image()?), PhantomData))
    }

    fn read_image(mut self) -> ImageResult<Vec<u8>> {
        match self.inner.read_image()? {
            tiff::decoder::DecodingResult::U8(v) => Ok(v),
            tiff::decoder::DecodingResult::U16(v) => Ok(vec_u16_into_u8(v)),
        }
    }
}

/// Encoder for tiff images
pub struct TiffEncoder<W> {
    w: W,
}

impl<W: Write + Seek> TiffEncoder<W> {
    /// Create a new encoder that writes its output to `w`
    pub fn new(w: W) -> TiffEncoder<W> {
        TiffEncoder { w }
    }

    /// Encodes the image `image`
    /// that has dimensions `width` and `height`
    /// and `ColorType` `c`.
    ///
    /// 16-bit colortypes are not yet supported.
    pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
        // TODO: 16bit support
        let mut encoder = tiff::encoder::TiffEncoder::new(self.w)?;
        match color {
            ColorType::L8 => encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data)?,
            ColorType::RGB => encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data)?,
            ColorType::RGBA => encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data)?,
            _ => return Err(ImageError::UnsupportedColor(color))
        }

        Ok(())
    }
}
<commit_msg>16bit bitdepths for tiff<commit_after>//! Decoding and Encoding of TIFF Images
//!
//! TIFF (Tagged Image File Format) is a versatile image format that supports
//! lossless and lossy compression.
//!
//! # Related Links
//! * <http://partners.adobe.com/public/developer/tiff/index.html> - The TIFF specification

extern crate tiff;

use std::io::{self, Cursor, Read, Write, Seek};
use std::marker::PhantomData;
use std::mem;

use color::ColorType;
use image::{ImageDecoder, ImageResult, ImageError};
use utils::vec_u16_into_u8;

/// Decoder for TIFF images.
pub struct TIFFDecoder<R>
    where R: Read + Seek
{
    dimensions: (u32, u32),
    colortype: ColorType,
    inner: tiff::decoder::Decoder<R>,
}

impl<R> TIFFDecoder<R>
    where R: Read + Seek
{
    /// Create a new TIFFDecoder.
    pub fn new(r: R) -> Result<TIFFDecoder<R>, ImageError> {
        let mut inner = tiff::decoder::Decoder::new(r)?;
        let dimensions = inner.dimensions()?;
        let colortype = inner.colortype()?.into();

        Ok(TIFFDecoder {
            dimensions,
            colortype,
            inner,
        })
    }
}

impl From<tiff::TiffError> for ImageError {
    fn from(err: tiff::TiffError) -> ImageError {
        match err {
            tiff::TiffError::IoError(err) => ImageError::IoError(err),
            tiff::TiffError::FormatError(desc) => ImageError::FormatError(desc.to_string()),
            tiff::TiffError::UnsupportedError(desc) => ImageError::UnsupportedError(desc.to_string()),
            tiff::TiffError::LimitsExceeded => ImageError::InsufficientMemory,
        }
    }
}

impl From<tiff::ColorType> for ColorType {
    fn from(ct: tiff::ColorType) -> ColorType {
        match ct {
            tiff::ColorType::Palette(depth) => ColorType::Unknown(depth),
            tiff::ColorType::Gray(1) => ColorType::L1,
            tiff::ColorType::Gray(8) => ColorType::L8,
            tiff::ColorType::Gray(16) => ColorType::L16,
            tiff::ColorType::GrayA(8) => ColorType::LA,
            tiff::ColorType::GrayA(16) => ColorType::LA16,
            tiff::ColorType::RGB(8) => ColorType::RGB,
            tiff::ColorType::RGB(16) => ColorType::RGB16,
            tiff::ColorType::RGBA(8) => ColorType::RGBA,
            tiff::ColorType::RGBA(16) => ColorType::RGBA16,
            tiff::ColorType::CMYK(_) => unimplemented!(),
            _ => unimplemented!(),
        }
    }
}

/// Wrapper struct around a `Cursor<Vec<u8>>`
pub struct TiffReader<R>(Cursor<Vec<u8>>, PhantomData<R>);
impl<R> Read for TiffReader<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.0.read(buf)
    }
    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        if self.0.position() == 0 && buf.is_empty() {
            mem::swap(buf, self.0.get_mut());
            Ok(buf.len())
        } else {
            self.0.read_to_end(buf)
        }
    }
}

impl<'a, R: 'a + Read + Seek> ImageDecoder<'a> for TIFFDecoder<R> {
    type Reader = TiffReader<R>;

    fn dimensions(&self) -> (u64, u64) {
        (self.dimensions.0 as u64, self.dimensions.1 as u64)
    }

    fn colortype(&self) -> ColorType {
        self.colortype
    }

    fn into_reader(self) -> ImageResult<Self::Reader> {
        Ok(TiffReader(Cursor::new(self.read_image()?), PhantomData))
    }

    fn read_image(mut self) -> ImageResult<Vec<u8>> {
        match self.inner.read_image()? {
            tiff::decoder::DecodingResult::U8(v) => Ok(v),
            tiff::decoder::DecodingResult::U16(v) => Ok(vec_u16_into_u8(v)),
        }
    }
}

/// Encoder for tiff images
pub struct TiffEncoder<W> {
    w: W,
}

impl<W: Write + Seek> TiffEncoder<W> {
    /// Create a new encoder that writes its output to `w`
    pub fn new(w: W) -> TiffEncoder<W> {
        TiffEncoder { w }
    }

    /// Encodes the image `image`
    /// that has dimensions `width` and `height`
    /// and `ColorType` `c`.
    ///
    /// 16-bit colortypes are not yet supported.
    pub fn encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> ImageResult<()> {
        // TODO: 16bit support
        let mut encoder = tiff::encoder::TiffEncoder::new(self.w)?;
        match color {
            ColorType::L8 => encoder.write_image::<tiff::encoder::colortype::Gray8>(width, height, data)?,
            ColorType::RGB => encoder.write_image::<tiff::encoder::colortype::RGB8>(width, height, data)?,
            ColorType::RGBA => encoder.write_image::<tiff::encoder::colortype::RGBA8>(width, height, data)?,
            _ => return Err(ImageError::UnsupportedColor(color))
        }

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


<commit_before>#[derive(Clone, Debug, Eq, PartialEq, Hash)]
//the indexes inside the enums are for flexibility. You can create an extra array for different
//types of particular tile type and index it with the index. For example you could have an array of
//different wall texture variations and have your level generator algorithm put random indexes in
//the walls so that the level would be more visually varied. You could also also have an array of
//different types of floors which each have their own properties (normal floor, icy floor, lava floor etc.)
pub enum Tile {
	Wall(usize),
	Floor(usize),
	Void(usize),
    Faction(usize),
}
<commit_msg>tiles had tabs and the faction enum which is soon going to be in its own enum<commit_after>#[derive(Clone, Debug, Eq, PartialEq, Hash)]
//the indexes inside the enums are for flexibility. You can create an extra array for different
//types of particular tile type and index it with the index. For example you could have an array of
//different wall texture variations and have your level generator algorithm put random indexes in
//the walls so that the level would be more visually varied. You could also also have an array of
//different types of floors which each have their own properties (normal floor, icy floor, lava floor etc.)
pub enum Tile {
    Wall(usize),
    Floor(usize),
    Void(usize),
}
<|endoftext|>


<commit_before>use countminsketch::CountMinSketch;
use std::collections::{BTreeSet, HashMap};
use std::collections::hash_map::Entry;
use std::cmp::Ordering;
use std::hash::Hash;
use std::rc::Rc;

struct TreeEntry<T>
where
    T: Eq + Ord,
{
    obj: Rc<T>,
    n: usize,
}

impl<T> Clone for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn clone(&self) -> Self {
        TreeEntry {
            obj: Rc::clone(&self.obj),
            n: self.n,
        }
    }
}

impl<T> PartialEq for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn eq(&self, other: &TreeEntry<T>) -> bool {
        self.obj == other.obj
    }
}

impl<T> Eq for TreeEntry<T>
where
    T: Eq + Ord,
{
}

impl<T> PartialOrd for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn partial_cmp(&self, other: &TreeEntry<T>) -> Option<Ordering> {
        match self.n.cmp(&other.n) {
            Ordering::Greater => Some(Ordering::Greater),
            Ordering::Less => Some(Ordering::Less),
            Ordering::Equal => Some(self.obj.cmp(&other.obj)),
        }
    }
}

impl<T> Ord for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn cmp(&self, other: &TreeEntry<T>) -> Ordering {
        match self.n.cmp(&other.n) {
            Ordering::Greater => Ordering::Greater,
            Ordering::Less => Ordering::Less,
            Ordering::Equal => self.obj.cmp(&other.obj),
        }
    }
}

/// Top-K implementation.
///
/// This data structure keeps the `k` most frequent data points of a stream.
pub struct TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    cms: CountMinSketch,
    obj2count: HashMap<Rc<T>, usize>,
    tree: BTreeSet<TreeEntry<T>>,
    k: usize,
}

impl<T> TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    /// Create new Top-K data structure.
    ///
    /// - `k`: number of elements to remember
    /// - `cms`: CountMinSketch that is used for guessing the frequency of elements not currently
    ///   hold. Refer to its documentation about parameter selection.
    ///
    /// Panics if `k == 0`.
    pub fn new(k: usize, cms: CountMinSketch) -> TopK<T> {
        assert!(k > 0);
        TopK {
            cms: cms,
            obj2count: HashMap::new(),
            tree: BTreeSet::new(),
            k: k,
        }
    }

    /// Number of data points to remember.
    pub fn k(&self) -> usize {
        return self.k;
    }

    /// Observe a data point.
    pub fn add(&mut self, obj: T) {
        // create Rc so we can use obj in multiple data structs
        let rc = Rc::new(obj);

        // always increase CountMinSketch counts
        self.cms.add(&rc);

        // check if entry is in top K
        let size = self.obj2count.len();
        let mut update_obj2count: Option<(usize, Rc<T>)> = None;
        match self.obj2count.entry(Rc::clone(&rc)) {
            Entry::Occupied(mut o) => {
                // it is => increase exact counter
                let mut n = o.get_mut();
                *n += 1;

                // update tree data
                let mut entry = TreeEntry {
                    obj: Rc::clone(&rc),
                    n: *n - 1,
                };
                self.tree.remove(&entry);
                entry.n += 1;
                self.tree.insert(entry);
            }
            Entry::Vacant(v) => {
                // it's not => check capicity
                if size < self.k {
                    // space left => add to top k
                    v.insert(1);
                    self.tree.insert(TreeEntry {
                        obj: Rc::clone(&rc),
                        n: 1,
                    });
                } else {
                    // not enough space => check if it would be a top k element
                    let count = self.cms.query_point(&rc);
                    let min: TreeEntry<T> = (*self.tree.iter().next().unwrap()).clone();
                    if count > min.n {
                        // => kick out minimal element of top k
                        self.tree.remove(&min);
                        self.tree.insert(TreeEntry {
                            obj: Rc::clone(&rc),
                            n: count,
                        });

                        // delay obj2count changes, because it is currently borrowed
                        update_obj2count = Some((count, min.obj));
                    }
                }
            }
        }

        // trick the borrow checker
        if let Some((count, obj)) = update_obj2count {
            self.obj2count.insert(rc, count);
            self.obj2count.remove(&obj);
        }
    }

    /// Returns top-k values.
    ///
    /// The result may contain less than `k` values if less than `k` unique data points where
    /// observed.
    pub fn values(&self) -> Vec<T> {
        self.tree.iter().map(|x| (*x.obj).clone()).collect()
    }

    /// Check whether the sampler is empty (i.e. no data points observer so far)
    pub fn is_empty(&self) -> bool {
        self.obj2count.is_empty()
    }
}

#[cfg(test)]
mod tests {
    use countminsketch::CountMinSketch;
    use super::TopK;

    #[test]
    fn getter() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let tk: TopK<usize> = TopK::new(2, cms);

        assert_eq!(tk.k(), 2);
    }

    #[test]
    fn add_1() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        assert_eq!(tk.values(), vec![1]);
    }

    #[test]
    fn add_2_same() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        tk.add(1);
        assert_eq!(tk.values(), vec![1]);
    }

    #[test]
    fn add_2_different() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        tk.add(2);
        assert_eq!(tk.values(), vec![1, 2]);
    }

    #[test]
    fn add_n() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        for i in 0..5 {
            tk.add(i);
        }
        for _ in 0..100 {
            tk.add(99);
        }
        for _ in 0..100 {
            tk.add(100);
        }
        for i in 0..5 {
            tk.add(i);
        }
        assert_eq!(tk.values(), vec![99, 100]);
    }

    #[test]
    fn is_empty() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);
        assert_eq!(tk.is_empty(), true);

        tk.add(0);
        assert_eq!(tk.is_empty(), false);
    }
}
<commit_msg>topk: impl clear<commit_after>use countminsketch::CountMinSketch;
use std::collections::{BTreeSet, HashMap};
use std::collections::hash_map::Entry;
use std::cmp::Ordering;
use std::hash::Hash;
use std::rc::Rc;

struct TreeEntry<T>
where
    T: Eq + Ord,
{
    obj: Rc<T>,
    n: usize,
}

impl<T> Clone for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn clone(&self) -> Self {
        TreeEntry {
            obj: Rc::clone(&self.obj),
            n: self.n,
        }
    }
}

impl<T> PartialEq for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn eq(&self, other: &TreeEntry<T>) -> bool {
        self.obj == other.obj
    }
}

impl<T> Eq for TreeEntry<T>
where
    T: Eq + Ord,
{
}

impl<T> PartialOrd for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn partial_cmp(&self, other: &TreeEntry<T>) -> Option<Ordering> {
        match self.n.cmp(&other.n) {
            Ordering::Greater => Some(Ordering::Greater),
            Ordering::Less => Some(Ordering::Less),
            Ordering::Equal => Some(self.obj.cmp(&other.obj)),
        }
    }
}

impl<T> Ord for TreeEntry<T>
where
    T: Eq + Ord,
{
    fn cmp(&self, other: &TreeEntry<T>) -> Ordering {
        match self.n.cmp(&other.n) {
            Ordering::Greater => Ordering::Greater,
            Ordering::Less => Ordering::Less,
            Ordering::Equal => self.obj.cmp(&other.obj),
        }
    }
}

/// Top-K implementation.
///
/// This data structure keeps the `k` most frequent data points of a stream.
pub struct TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    cms: CountMinSketch,
    obj2count: HashMap<Rc<T>, usize>,
    tree: BTreeSet<TreeEntry<T>>,
    k: usize,
}

impl<T> TopK<T>
where
    T: Clone + Eq + Hash + Ord,
{
    /// Create new Top-K data structure.
    ///
    /// - `k`: number of elements to remember
    /// - `cms`: CountMinSketch that is used for guessing the frequency of elements not currently
    ///   hold. Refer to its documentation about parameter selection.
    ///
    /// Panics if `k == 0`.
    pub fn new(k: usize, cms: CountMinSketch) -> TopK<T> {
        assert!(k > 0);
        TopK {
            cms: cms,
            obj2count: HashMap::new(),
            tree: BTreeSet::new(),
            k: k,
        }
    }

    /// Number of data points to remember.
    pub fn k(&self) -> usize {
        return self.k;
    }

    /// Observe a data point.
    pub fn add(&mut self, obj: T) {
        // create Rc so we can use obj in multiple data structs
        let rc = Rc::new(obj);

        // always increase CountMinSketch counts
        self.cms.add(&rc);

        // check if entry is in top K
        let size = self.obj2count.len();
        let mut update_obj2count: Option<(usize, Rc<T>)> = None;
        match self.obj2count.entry(Rc::clone(&rc)) {
            Entry::Occupied(mut o) => {
                // it is => increase exact counter
                let mut n = o.get_mut();
                *n += 1;

                // update tree data
                let mut entry = TreeEntry {
                    obj: Rc::clone(&rc),
                    n: *n - 1,
                };
                self.tree.remove(&entry);
                entry.n += 1;
                self.tree.insert(entry);
            }
            Entry::Vacant(v) => {
                // it's not => check capicity
                if size < self.k {
                    // space left => add to top k
                    v.insert(1);
                    self.tree.insert(TreeEntry {
                        obj: Rc::clone(&rc),
                        n: 1,
                    });
                } else {
                    // not enough space => check if it would be a top k element
                    let count = self.cms.query_point(&rc);
                    let min: TreeEntry<T> = (*self.tree.iter().next().unwrap()).clone();
                    if count > min.n {
                        // => kick out minimal element of top k
                        self.tree.remove(&min);
                        self.tree.insert(TreeEntry {
                            obj: Rc::clone(&rc),
                            n: count,
                        });

                        // delay obj2count changes, because it is currently borrowed
                        update_obj2count = Some((count, min.obj));
                    }
                }
            }
        }

        // trick the borrow checker
        if let Some((count, obj)) = update_obj2count {
            self.obj2count.insert(rc, count);
            self.obj2count.remove(&obj);
        }
    }

    /// Returns top-k values.
    ///
    /// The result may contain less than `k` values if less than `k` unique data points where
    /// observed.
    pub fn values(&self) -> Vec<T> {
        self.tree.iter().map(|x| (*x.obj).clone()).collect()
    }

    /// Check whether the sampler is empty (i.e. no data points observer so far)
    pub fn is_empty(&self) -> bool {
        self.obj2count.is_empty()
    }

    /// Clear sampler, so that it behaves like it never observed any data points.
    pub fn clear(&mut self) {
        self.cms.clear();
        self.tree.clear();
        self.obj2count.clear();
    }
}

#[cfg(test)]
mod tests {
    use countminsketch::CountMinSketch;
    use super::TopK;

    #[test]
    fn getter() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let tk: TopK<usize> = TopK::new(2, cms);

        assert_eq!(tk.k(), 2);
    }

    #[test]
    fn add_1() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        assert_eq!(tk.values(), vec![1]);
    }

    #[test]
    fn add_2_same() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        tk.add(1);
        assert_eq!(tk.values(), vec![1]);
    }

    #[test]
    fn add_2_different() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        tk.add(1);
        tk.add(2);
        assert_eq!(tk.values(), vec![1, 2]);
    }

    #[test]
    fn add_n() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);

        for i in 0..5 {
            tk.add(i);
        }
        for _ in 0..100 {
            tk.add(99);
        }
        for _ in 0..100 {
            tk.add(100);
        }
        for i in 0..5 {
            tk.add(i);
        }
        assert_eq!(tk.values(), vec![99, 100]);
    }

    #[test]
    fn is_empty() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);
        assert_eq!(tk.is_empty(), true);

        tk.add(0);
        assert_eq!(tk.is_empty(), false);
    }

    #[test]
    fn clear() {
        let cms = CountMinSketch::<usize>::with_params(10, 20);
        let mut tk = TopK::new(2, cms);
        tk.add(0);

        tk.clear();
        assert_eq!(tk.is_empty(), true);

        tk.add(1);
        assert_eq!(tk.values(), vec![1]);
    }
}
<|endoftext|>


<commit_before>use crate::{Browser, Error, ErrorKind, Result};
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

macro_rules! try_browser {
    ( $name:expr, $( $arg:expr ),+ ) => {
        for_matching_path($name, |pb| {
            let mut cmd = Command::new(pb);
            $(
                cmd.arg($arg);
            )+
            run_command(&mut cmd, !is_text_browser(&pb))
        })
    }
}

/// Deal with opening of browsers on Linux and *BSD - currently supports only the default browser
///
/// The mechanism of opening the default browser is as follows:
/// 1. Attempt to use $BROWSER env var if available
/// 2. Attempt to use xdg-open
/// 3. Attempt to use window manager specific commands, like gnome-open, kde-open etc.
/// 4. Fallback to x-www-browser
#[inline]
pub fn open_browser_internal(_: Browser, url: &str) -> Result<()> {
    // we first try with the $BROWSER env
    try_with_browser_env(url)
        // then we try with xdg-open
        .or_else(|_| try_browser!("xdg-open", url))
        // else do desktop specific stuff
        .or_else(|r| match guess_desktop_env() {
            "kde" => try_browser!("kde-open", url)
                .or_else(|_| try_browser!("kde-open5", url))
                .or_else(|_| try_browser!("kfmclient", "newTab", url)),

            "gnome" => try_browser!("gio", "open", url)
                .or_else(|_| try_browser!("gvfs-open", url))
                .or_else(|_| try_browser!("gnome-open", url)),

            "mate" => try_browser!("gio", "open", url)
                .or_else(|_| try_browser!("gvfs-open", url))
                .or_else(|_| try_browser!("mate-open", url)),

            "xfce" => try_browser!("exo-open", url)
                .or_else(|_| try_browser!("gio", "open", url))
                .or_else(|_| try_browser!("gvfs-open", url)),

            _ => Err(r),
        })
        // at the end, we'll try x-www-browser and return the result as is
        .or_else(|_| try_browser!("x-www-browser", url))
        // if all above failed, map error to not found
        .map_err(|_| {
            Error::new(
                ErrorKind::NotFound,
                "No valid browsers detected. You can specify one in BROWSERS environment variable",
            )
        })
        // and convert a successful result into a ()
        .map(|_| ())
}

#[inline]
fn try_with_browser_env(url: &str) -> Result<()> {
    // $BROWSER can contain ':' delimited options, each representing a potential browser command line
    for browser in std::env::var("BROWSER")
        .unwrap_or_else(|_| String::from(""))
        .split(':')
    {
        if !browser.is_empty() {
            // each browser command can have %s to represent URL, while %c needs to be replaced
            // with ':' and %% with '%'
            let cmdline = browser
                .replace("%s", url)
                .replace("%c", ":")
                .replace("%%", "%");
            let cmdarr: Vec<&str> = cmdline.split_ascii_whitespace().collect();
            let browser_cmd = cmdarr[0];
            let env_exit = for_matching_path(browser_cmd, |pb| {
                let mut cmd = Command::new(pb);
                for arg in cmdarr.iter().skip(1) {
                    cmd.arg(arg);
                }
                if !browser.contains("%s") {
                    // append the url as an argument only if it was not already set via %s
                    cmd.arg(url);
                }
                run_command(&mut cmd, !is_text_browser(pb))
            });
            if env_exit.is_ok() {
                return Ok(());
            }
        }
    }
    Err(Error::new(
        ErrorKind::NotFound,
        "No valid browser configured in BROWSER environment variable",
    ))
}

/// Detect the desktop environment
#[inline]
fn guess_desktop_env() -> &'static str {
    let unknown = "unknown";
    let xcd: String = std::env::var("XDG_CURRENT_DESKTOP")
        .unwrap_or_else(|_| unknown.into())
        .to_ascii_lowercase();
    let dsession: String = std::env::var("DESKTOP_SESSION")
        .unwrap_or_else(|_| unknown.into())
        .to_ascii_lowercase();

    if xcd.contains("gnome") || xcd.contains("cinnamon") || dsession.contains("gnome") {
        // GNOME and its derivatives
        "gnome"
    } else if xcd.contains("kde")
        || std::env::var("KDE_FULL_SESSION").is_ok()
        || std::env::var("KDE_SESSION_VERSION").is_ok()
    {
        // KDE: https://userbase.kde.org/KDE_System_Administration/Environment_Variables#Automatically_Set_Variables
        "kde"
    } else if xcd.contains("mate") || dsession.contains("mate") {
        // We'll treat MATE as distinct from GNOME due to mate-open
        "mate"
    } else if xcd.contains("xfce") || dsession.contains("xfce") {
        // XFCE
        "xfce"
    } else {
        // All others
        unknown
    }
}

/// Returns true if specified command refers to a known list of text browsers
#[inline]
fn is_text_browser(pb: &Path) -> bool {
    for browser in TEXT_BROWSERS.iter() {
        if pb.ends_with(&browser) {
            return true;
        }
    }
    false
}

#[inline]
fn for_matching_path<F>(name: &str, op: F) -> Result<()>
where
    F: FnOnce(&PathBuf) -> Result<()>,
{
    let err = Err(Error::new(ErrorKind::NotFound, "command not found"));

    // if the name already includes path separator, we should not try to do a PATH search on it
    // as it's likely an absolutely or relative name, so we treat it as such.
    if name.contains(std::path::MAIN_SEPARATOR) {
        let pb = std::path::PathBuf::from(name);
        if let Ok(metadata) = pb.metadata() {
            if metadata.is_file() && metadata.permissions().mode() & 0o111 != 0 {
                return op(&pb);
            }
        } else {
            return err;
        }
    } else {
        // search for this name inside PATH
        if let Ok(path) = std::env::var("PATH") {
            for entry in path.split(':') {
                let mut pb = std::path::PathBuf::from(entry);
                pb.push(name);
                if let Ok(metadata) = pb.metadata() {
                    if metadata.is_file() && metadata.permissions().mode() & 0o111 != 0 {
                        return op(&pb);
                    }
                }
            }
        }
    }
    // return the not found err, if we didn't find anything above
    err
}

/// Run the specified command in foreground/background
#[inline]
fn run_command(cmd: &mut Command, background: bool) -> Result<()> {
    if background {
        // if we're in background, set stdin/stdout to null and spawn a child, as we're
        // not supposed to have any interaction.
        cmd.stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()
            .map(|_| ())
    } else {
        // if we're in foreground, use status() instead of spawn(), as we'd like to wait
        // till completion
        cmd.status().and_then(|status| {
            if status.success() {
                Ok(())
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    "command present but exited unsuccessfully",
                ))
            }
        })
    }
}

static TEXT_BROWSERS: [&str; 9] = [
    "lynx", "links", "links2", "elinks", "w3m", "eww", "netrik", "retawq", "curl",
];
<commit_msg>handle haiku explicitly under unix<commit_after>use crate::{Browser, Error, ErrorKind, Result};
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};

macro_rules! try_browser {
    ( $name:expr, $( $arg:expr ),+ ) => {
        for_matching_path($name, |pb| {
            let mut cmd = Command::new(pb);
            $(
                cmd.arg($arg);
            )+
            run_command(&mut cmd, !is_text_browser(&pb))
        })
    }
}

/// Deal with opening of browsers on Linux and *BSD - currently supports only the default browser
///
/// The mechanism of opening the default browser is as follows:
/// 1. Attempt to use $BROWSER env var if available
/// 2. Attempt to use xdg-open
/// 3. Attempt to use window manager specific commands, like gnome-open, kde-open etc.
/// 4. Fallback to x-www-browser
#[inline]
pub fn open_browser_internal(_: Browser, url: &str) -> Result<()> {
    // we first try with the $BROWSER env
    try_with_browser_env(url)
        // allow for haiku's open specifically
        .or_else(|_| try_haiku(url))
        // then we try with xdg-open
        .or_else(|_| try_browser!("xdg-open", url))
        // else do desktop specific stuff
        .or_else(|r| match guess_desktop_env() {
            "kde" => try_browser!("kde-open", url)
                .or_else(|_| try_browser!("kde-open5", url))
                .or_else(|_| try_browser!("kfmclient", "newTab", url)),

            "gnome" => try_browser!("gio", "open", url)
                .or_else(|_| try_browser!("gvfs-open", url))
                .or_else(|_| try_browser!("gnome-open", url)),

            "mate" => try_browser!("gio", "open", url)
                .or_else(|_| try_browser!("gvfs-open", url))
                .or_else(|_| try_browser!("mate-open", url)),

            "xfce" => try_browser!("exo-open", url)
                .or_else(|_| try_browser!("gio", "open", url))
                .or_else(|_| try_browser!("gvfs-open", url)),

            _ => Err(r),
        })
        // at the end, we'll try x-www-browser and return the result as is
        .or_else(|_| try_browser!("x-www-browser", url))
        // if all above failed, map error to not found
        .map_err(|_| {
            Error::new(
                ErrorKind::NotFound,
                "No valid browsers detected. You can specify one in BROWSERS environment variable",
            )
        })
        // and convert a successful result into a ()
        .map(|_| ())
}

#[inline]
fn try_with_browser_env(url: &str) -> Result<()> {
    // $BROWSER can contain ':' delimited options, each representing a potential browser command line
    for browser in std::env::var("BROWSER")
        .unwrap_or_else(|_| String::from(""))
        .split(':')
    {
        if !browser.is_empty() {
            // each browser command can have %s to represent URL, while %c needs to be replaced
            // with ':' and %% with '%'
            let cmdline = browser
                .replace("%s", url)
                .replace("%c", ":")
                .replace("%%", "%");
            let cmdarr: Vec<&str> = cmdline.split_ascii_whitespace().collect();
            let browser_cmd = cmdarr[0];
            let env_exit = for_matching_path(browser_cmd, |pb| {
                let mut cmd = Command::new(pb);
                for arg in cmdarr.iter().skip(1) {
                    cmd.arg(arg);
                }
                if !browser.contains("%s") {
                    // append the url as an argument only if it was not already set via %s
                    cmd.arg(url);
                }
                run_command(&mut cmd, !is_text_browser(pb))
            });
            if env_exit.is_ok() {
                return Ok(());
            }
        }
    }
    Err(Error::new(
        ErrorKind::NotFound,
        "No valid browser configured in BROWSER environment variable",
    ))
}

/// Detect the desktop environment
#[inline]
fn guess_desktop_env() -> &'static str {
    let unknown = "unknown";
    let xcd: String = std::env::var("XDG_CURRENT_DESKTOP")
        .unwrap_or_else(|_| unknown.into())
        .to_ascii_lowercase();
    let dsession: String = std::env::var("DESKTOP_SESSION")
        .unwrap_or_else(|_| unknown.into())
        .to_ascii_lowercase();

    if xcd.contains("gnome") || xcd.contains("cinnamon") || dsession.contains("gnome") {
        // GNOME and its derivatives
        "gnome"
    } else if xcd.contains("kde")
        || std::env::var("KDE_FULL_SESSION").is_ok()
        || std::env::var("KDE_SESSION_VERSION").is_ok()
    {
        // KDE: https://userbase.kde.org/KDE_System_Administration/Environment_Variables#Automatically_Set_Variables
        "kde"
    } else if xcd.contains("mate") || dsession.contains("mate") {
        // We'll treat MATE as distinct from GNOME due to mate-open
        "mate"
    } else if xcd.contains("xfce") || dsession.contains("xfce") {
        // XFCE
        "xfce"
    } else {
        // All others
        unknown
    }
}

// Handle Haiku explicitly, as it uses an "open" command, similar to macos
// but on other Unixes, open ends up translating to shell open fd
#[inline]
fn try_haiku(url: &str) -> Result<()> {
    if cfg!(target_os = "haiku") {
        try_browser!("open", url).map(|_| ())
    } else {
        Err(Error::new(ErrorKind::NotFound, "Not on haiku"))
    }
}

/// Returns true if specified command refers to a known list of text browsers
#[inline]
fn is_text_browser(pb: &Path) -> bool {
    for browser in TEXT_BROWSERS.iter() {
        if pb.ends_with(&browser) {
            return true;
        }
    }
    false
}

#[inline]
fn for_matching_path<F>(name: &str, op: F) -> Result<()>
where
    F: FnOnce(&PathBuf) -> Result<()>,
{
    let err = Err(Error::new(ErrorKind::NotFound, "command not found"));

    // if the name already includes path separator, we should not try to do a PATH search on it
    // as it's likely an absolutely or relative name, so we treat it as such.
    if name.contains(std::path::MAIN_SEPARATOR) {
        let pb = std::path::PathBuf::from(name);
        if let Ok(metadata) = pb.metadata() {
            if metadata.is_file() && metadata.permissions().mode() & 0o111 != 0 {
                return op(&pb);
            }
        } else {
            return err;
        }
    } else {
        // search for this name inside PATH
        if let Ok(path) = std::env::var("PATH") {
            for entry in path.split(':') {
                let mut pb = std::path::PathBuf::from(entry);
                pb.push(name);
                if let Ok(metadata) = pb.metadata() {
                    if metadata.is_file() && metadata.permissions().mode() & 0o111 != 0 {
                        return op(&pb);
                    }
                }
            }
        }
    }
    // return the not found err, if we didn't find anything above
    err
}

/// Run the specified command in foreground/background
#[inline]
fn run_command(cmd: &mut Command, background: bool) -> Result<()> {
    if background {
        // if we're in background, set stdin/stdout to null and spawn a child, as we're
        // not supposed to have any interaction.
        cmd.stdin(Stdio::null())
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .spawn()
            .map(|_| ())
    } else {
        // if we're in foreground, use status() instead of spawn(), as we'd like to wait
        // till completion
        cmd.status().and_then(|status| {
            if status.success() {
                Ok(())
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    "command present but exited unsuccessfully",
                ))
            }
        })
    }
}

static TEXT_BROWSERS: [&str; 9] = [
    "lynx", "links", "links2", "elinks", "w3m", "eww", "netrik", "retawq", "curl",
];
<|endoftext|>


<commit_before>//! Utilities for validating data.

/// Validates an over-the-air callsign. APRS-IS is more forgiving.
///
/// Valid callsigns are between 4 and 7 alphanumeric characters with at most a single hyphen `-`
/// separating the callsign and the SSID suffix. This function validates the callsign's length,
/// composition and SSID.
///
/// # Examples
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::valid_callsign;
/// # fn main() {
/// assert_eq!(valid_callsign("W2GMD"), true);
/// assert_eq!(valid_callsign("W2GMD-2"), true);
/// assert_eq!(valid_callsign("W2GMD*"), true);
/// # }
/// ```
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::valid_callsign;
/// # fn main() {
/// assert_eq!(valid_callsign("W2GMD--1"), false);
/// assert_eq!(valid_callsign("W2GMD- 1"), false);
/// assert_eq!(valid_callsign("W2*MD-8"), false);
/// # }
/// ```
///
pub fn valid_callsign(callsign: &str) -> bool {
    let mut callsign: &str = callsign.trim_matches(|x| x == '*' || (x as char).is_whitespace());
    let mut ssid: &str = "0";

    // Ensure single separator between callsign and SSID
    match callsign.matches('-').count() {
        0 => {},
        1 => {
            let call_components: Vec<&str> = callsign.split('-').collect();
            callsign = call_components[0];
            ssid = call_components[1];
        },
        _ => {return false;},
    }
    // Validate callsign
    match callsign.len() {
        3...6 => {},
        _ => {return false;},
    }
    for chr in callsign.chars() {
        if !chr.is_alphanumeric() {
            return false;
        }
    }
    // Validate SSID
    match ssid.len() {
        1...2 => {},
        _ => {return false;}
    }
    for chr in ssid.chars() {
        if !chr.is_numeric() {
            return false;
        }
    }
    match ssid.parse::<usize>()  {
        Err(e) => {return false;},
        Ok(s) => {
            match s {
                0...15 => {},
                _ => {return false;},
            }
        },
    }
    // Callsign and SSID have passed all checks, so return true
    true
}
<commit_msg>Add APRS-IS passcode hash function. Doctests pass.<commit_after>//! Utilities for validating data.

/// Validates an over-the-air callsign. APRS-IS is more forgiving.
///
/// Valid callsigns are between 4 and 7 alphanumeric characters with at most a single hyphen `-`
/// separating the callsign and the SSID suffix. This function validates the callsign's length,
/// composition and SSID.
///
/// # Examples
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::valid_callsign;
/// # fn main() {
/// assert_eq!(valid_callsign("W2GMD"), true);
/// assert_eq!(valid_callsign("W2GMD-2"), true);
/// assert_eq!(valid_callsign("W2GMD*"), true);
/// # }
/// ```
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::valid_callsign;
/// # fn main() {
/// assert_eq!(valid_callsign("W2GMD--1"), false);
/// assert_eq!(valid_callsign("W2GMD- 1"), false);
/// assert_eq!(valid_callsign("W2*MD-8"), false);
/// # }
/// ```
///
pub fn valid_callsign(callsign: &str) -> bool {
    let mut callsign: &str = callsign.trim_matches(|x| x == '*' || (x as char).is_whitespace());
    let mut ssid: &str = "0";

    // Ensure single separator between callsign and SSID
    match callsign.matches('-').count() {
        0 => {},
        1 => {
            let call_components: Vec<&str> = callsign.split('-').collect();
            callsign = call_components[0];
            ssid = call_components[1];
        },
        _ => {return false;},
    }
    // Validate callsign
    match callsign.len() {
        3...6 => {},
        _ => {return false;},
    }
    for chr in callsign.chars() {
        if !chr.is_alphanumeric() {
            return false;
        }
    }
    // Validate SSID
    match ssid.len() {
        1...2 => {},
        _ => {return false;}
    }
    for chr in ssid.chars() {
        if !chr.is_numeric() {
            return false;
        }
    }
    match ssid.parse::<usize>()  {
        Err(e) => {return false;},
        Ok(s) => {
            match s {
                0...15 => {},
                _ => {return false;},
            }
        },
    }
    // Callsign and SSID have passed all checks, so return true
    true
}

/// Hashing algorithm to generate a passcode for APRS-IS.
/// Original code by Steve Dimse in the [aprsd package][1].
///
/// ```rust
/// # extern crate aprs;
/// # use aprs::util::aprspass;
/// # fn main() {
/// // Callsigns can be uppercase...
/// assert_eq!(aprspass("W2GMD"), Some(10141));
/// // ...or lowercase
/// assert_eq!(aprspass("n6gso"), Some(13703));
/// # }
/// ```
///
/// ```text
/// short doHash(const char *theCall) {
///     char rootCall[10];            // need to copy call to remove ssid from parse
///     char *p1 = rootCall;
///
///     while ((*theCall != '-') && (*theCall != 0)) *p1++ = toupper(*theCall++);
///     *p1 = 0;
///
///     short hash = 0x73e2;          // Initialize with the key value
///     short i = 0;
///     short len = strlen(rootCall);
///     char *ptr = rootCall;
///
///     while (i<len) {               // Loop through the string two bytes at a time
///         hash ^= (*ptr++)<<8;      // xor high byte with accumulated hash
///         hash ^= (*ptr++);         // xor low byte with accumulated hash
///         i += 2;
///     }
///     return hash & 0x7fff;         // mask off the high bit so number is always positive
/// }
/// ```
///
/// [1]: ftp://ftp.tapr.org/software_lib/Linux/aprsd/
pub fn aprspass(callsign: &str) -> Option<u16> {
    match valid_callsign(callsign) {
        false => return None,
        true => (),
    }
    let mut hash: u16 = 0x73e2;

    for chunk in callsign.to_uppercase().bytes().collect::<Vec<u8>>().chunks(2) {
        match chunk.len() {
            1 => {
                hash = hash ^ ((chunk[0] as u16) << 8);
            },
            2 => {
                hash = hash ^ ((chunk[0] as u16) << 8);
                hash = hash ^ (chunk[1] as u16);
            },
            _ => {},
        }
    }
    Some(hash & 0x7fff)
}
<|endoftext|>


<commit_before>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// 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 std::io;
use std::marker::PhantomData;
use std::sync::mpsc;

pub trait Consume {
    type Item;
    type Error;
    fn consume(&mut self, value: Self::Item) -> Result<(), Self::Error>;

    fn map<T, F>(self, f: F) -> ConsumeMap<T, Self, F>
    where F: FnMut(T) -> Self::Item, Self: Sized {
        ConsumeMap {
            _phantom: PhantomData,
            consumer: self,
            mapping: f
        }
    }
}

impl<T> Consume for mpsc::Sender<T> {
    type Item = T;
    type Error = io::Error;

    fn consume(&mut self, value: T) -> Result<(), Self::Error> {
        self.send(value).map_err(|_| {
            io::Error::new(io::ErrorKind::BrokenPipe, "cannot write to mpsc::Sender")
        })
    }
}

pub struct ConsumeMap<T, C, F> where C: Consume, F: FnMut(T) -> C::Item {
    _phantom: PhantomData<T>,
    consumer: C,
    mapping: F
}

impl<T, C, F> Consume for ConsumeMap<T, C, F> where C: Consume, F: FnMut(T) -> C::Item {
    type Item = T;
    type Error = C::Error;

    fn consume(&mut self, value: T) -> Result<(), Self::Error> {
        let mapped = (self.mapping)(value);
        self.consumer.consume(mapped)
    }
}

#[cfg(test)]
mod tests {
    use std::sync::mpsc;

    use super::*;

    #[test]
    fn should_map_consumer() {
        let (tx, rx) = mpsc::channel::<usize>();
        let mut mapped = tx.map(|s: &str| s.len());
        mapped.consume("foobar");
        assert_eq!(rx.recv().unwrap(), 6);
    }
}
<commit_msg>Implement `Consume` for `mio::Sender`<commit_after>//
// FlightVars
// Copyright (c) 2015, 2016 Alvaro Polo
//
// 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 std::io;
use std::marker::PhantomData;
use std::sync::mpsc;

use mio;

pub trait Consume {
    type Item;
    type Error;
    fn consume(&mut self, value: Self::Item) -> Result<(), Self::Error>;

    fn map<T, F>(self, f: F) -> ConsumeMap<T, Self, F>
    where F: FnMut(T) -> Self::Item, Self: Sized {
        ConsumeMap {
            _phantom: PhantomData,
            consumer: self,
            mapping: f
        }
    }
}

impl<T> Consume for mpsc::Sender<T> {
    type Item = T;
    type Error = io::Error;

    fn consume(&mut self, value: T) -> Result<(), Self::Error> {
        self.send(value).map_err(|_| {
            io::Error::new(io::ErrorKind::BrokenPipe, "cannot write to mpsc::Sender")
        })
    }
}

impl<T> Consume for mio::Sender<T> where T: Send {
    type Item = T;
    type Error = io::Error;

    fn consume(&mut self, value: T) -> Result<(), Self::Error> {
        self.send(value).map_err(|_| {
            io::Error::new(io::ErrorKind::BrokenPipe, "cannot write to mio::Sender")
        })
    }
}

pub struct ConsumeMap<T, C, F> where C: Consume, F: FnMut(T) -> C::Item {
    _phantom: PhantomData<T>,
    consumer: C,
    mapping: F
}

impl<T, C, F> Consume for ConsumeMap<T, C, F> where C: Consume, F: FnMut(T) -> C::Item {
    type Item = T;
    type Error = C::Error;

    fn consume(&mut self, value: T) -> Result<(), Self::Error> {
        let mapped = (self.mapping)(value);
        self.consumer.consume(mapped)
    }
}

#[cfg(test)]
mod tests {
    use std::sync::mpsc;

    use super::*;

    #[test]
    fn should_map_consumer() {
        let (tx, rx) = mpsc::channel::<usize>();
        let mut mapped = tx.map(|s: &str| s.len());
        mapped.consume("foobar");
        assert_eq!(rx.recv().unwrap(), 6);
    }
}
<|endoftext|>


<commit_before>struct WorkUnit {
    arg: int,
    callback: Sender<int>,
}

struct WorkQueue {
    enqueue_work: Sender<WorkUnit>,
    worker_count: int,
}

impl WorkQueue {
    pub fn create(worker_count: int) -> WorkQueue {
        use native;
        use std::comm::channel;

        let (want_work, worker_wants_work) = channel::<Sender<WorkUnit>>();
        let (enqueue_work, have_new_job) = channel::<WorkUnit>();

        for _ in range(0, worker_count) {
            // worker
            let want_work_copy = want_work.clone();
            native::task::spawn(proc() {
                let want_work = want_work_copy;
                loop {
                    let (reply_with_work, get_work_unit) = channel::<WorkUnit>();
                    want_work.send(reply_with_work);
                    let work_unit = get_work_unit.recv();
                    if work_unit.arg == -1 {
                        return;
                    }
                    let rv = work_unit.arg * 2;
                    work_unit.callback.send(rv);
                }
            });
        }

        // dispatcher
        native::task::spawn(proc() {
            let mut worker_count = worker_count;
            let have_new_job = have_new_job;
            let worker_wants_work = worker_wants_work;
            loop {
                let unit = have_new_job.recv();
                let worker = worker_wants_work.recv();
                if unit.arg == -1 {
                    worker_count -= 1;
                }
                worker.send(unit);
                if worker_count == 0 {
                    return;
                }
            }
        });

        return WorkQueue{enqueue_work: enqueue_work, worker_count: worker_count};
    }

    pub fn execute(&self, arg: int) -> Receiver<int> {
        let (reply_with_rv, wait_for_rv) = channel::<int>();
        self.enqueue_work.send(WorkUnit{arg: arg, callback: reply_with_rv});
        return wait_for_rv;
    }
}

impl Drop for WorkQueue {
    fn drop(&mut self) {
        for _ in range(0, self.worker_count) {
            let (reply_with_rv, _) = channel::<int>();
            self.enqueue_work.send(WorkUnit{arg: -1, callback: reply_with_rv});
        }
    }
}

#[test]
fn test_queue() {
    let queue = WorkQueue::create(3);
    let mut promise_list: ~[Receiver<int>] = ~[];
    for c in range(0, 10) {
        let rv = queue.execute(c);
        promise_list.push(rv);
    }
    let return_list = promise_list.map(|promise| promise.recv());
    assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
}
<commit_msg>send MessageToWorker instead of raw integer<commit_after>struct WorkUnit {
    arg: int,
    callback: Sender<int>,
}

enum MessageToWorker {
    Work(WorkUnit),
    Halt,
}

struct WorkQueue {
    enqueue_work: Sender<MessageToWorker>,
    worker_count: int,
}

impl WorkQueue {
    pub fn create(worker_count: int) -> WorkQueue {
        use native;
        use std::comm::channel;

        let (want_work, worker_wants_work) = channel::<Sender<MessageToWorker>>();
        let (enqueue_work, have_new_job) = channel::<MessageToWorker>();

        for _ in range(0, worker_count) {
            // worker
            let want_work_copy = want_work.clone();
            native::task::spawn(proc() {
                let want_work = want_work_copy;
                loop {
                    let (reply_with_work, get_work_unit) = channel::<MessageToWorker>();
                    want_work.send(reply_with_work);
                    let work_unit = match get_work_unit.recv() {
                        Work(wu) => wu,
                        Halt     => return
                    };
                    let rv = work_unit.arg * 2;
                    work_unit.callback.send(rv);
                }
            });
        }

        // dispatcher
        native::task::spawn(proc() {
            let mut worker_count = worker_count;
            let have_new_job = have_new_job;
            let worker_wants_work = worker_wants_work;
            loop {
                let message_to_worker = have_new_job.recv();
                let worker = worker_wants_work.recv();
                match message_to_worker {
                    Halt => worker_count -= 1,
                    _    => {}
                }
                worker.send(message_to_worker);
                if worker_count == 0 {
                    return;
                }
            }
        });

        return WorkQueue{enqueue_work: enqueue_work, worker_count: worker_count};
    }

    pub fn execute(&self, arg: int) -> Receiver<int> {
        let (reply_with_rv, wait_for_rv) = channel::<int>();
        self.enqueue_work.send(Work(WorkUnit{arg: arg, callback: reply_with_rv}));
        return wait_for_rv;
    }
}

impl Drop for WorkQueue {
    fn drop(&mut self) {
        for _ in range(0, self.worker_count) {
            self.enqueue_work.send(Halt);
        }
    }
}

#[test]
fn test_queue() {
    let queue = WorkQueue::create(3);
    let mut promise_list: ~[Receiver<int>] = ~[];
    for c in range(0, 10) {
        let rv = queue.execute(c);
        promise_list.push(rv);
    }
    let return_list = promise_list.map(|promise| promise.recv());
    assert_eq!(return_list, ~[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]);
}
<|endoftext|>