<commit_before>#![feature(plugin, custom_derive, decl_macro)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

pub mod ip;

pub mod slack;

pub mod settings {
    extern crate config;

    #[derive(Deserialize)]
    pub struct Settings {
        pub verification_token: String,
        pub api_token: String,
    }

    impl Settings {
        pub fn new() -> Result<Settings, config::ConfigError> {
            let mut settings = config::Config::new();
            settings.merge(config::File::with_name("settings"))?;
            settings.try_into::<Settings>()
        }
    }
}

pub enum Response {
    Text(String),
    Json(serde_json::Value),
}

pub fn handle_command(
    settings: &settings::Settings,
    command: &str,
    data: slack::slash_command::Request,
) -> Result<serde_json::Value, Box<std::error::Error>> {
    if settings.verification_token != data.token {
        return Ok(json!({"text": "Validation error".to_owned()}));
    }

    let result = match command {
        "add" => add_command(settings, data),
        "get" => get_command(&data),
        _ => Ok(Response::Text("Nope".to_owned())),
    }?;

    match result {
        Response::Text(t) => Ok(json!({ "text": t })),
        Response::Json(j) => Ok(j),
    }
}

pub fn handle_submission(
    settings: &settings::Settings,
    submission: slack::dialog::Submission,
) -> Result<String, Box<std::error::Error>> {
    if settings.verification_token != submission.token {
        return Ok("Validation error".to_owned());
    }

    match submission.callback_id.as_ref() {
        "add" => add_submission(submission),
        _ => Ok("".to_owned()),
    }
}

fn add_command(
    settings: &settings::Settings,
    data: slack::slash_command::Request,
) -> Result<Response, Box<std::error::Error>> {
    show_add_dialog(settings, data)?;
    Ok(Response::Text("Dialog opend!".to_owned()))
}

fn show_add_dialog(
    settings: &settings::Settings,
    data: slack::slash_command::Request,
) -> Result<(), Box<std::error::Error>> {
    use slack::dialog::*;
    let mut dialog = Dialog::new("add".to_owned(), "IP 추가".to_owned());

    dialog.elements.push(generate_ip_text(None).into_json()?);
    dialog
        .elements
        .push(generate_domain_text(None).into_json()?);
    dialog
        .elements
        .push(generate_using_select(None).into_json()?);
    dialog
        .elements
        .push(generate_open_ports_text(None).into_json()?);
    dialog
        .elements
        .push(generate_description_textarea(None).into_json()?);

    let request = OpenRequest {
        token: settings.api_token.clone(),
        dialog,
        trigger_id: data.trigger_id,
    };
    open(request)?;

    Ok(())
}

fn get_command(data: &slack::slash_command::Request) -> Result<Response, Box<std::error::Error>> {
    use ip::get;
    if data.text.is_empty() {
        return Ok(Response::Text("Validation error".to_owned()));
    }
    let entry = get(&data.text);
    match entry {
        Some(e) => Ok(Response::Json(serde_json::to_value(show_get_info(&e))?)),
        None => Ok(Response::Text("IP not found".to_owned())),
    }
}

fn add_submission(submission: slack::dialog::Submission) -> Result<String, Box<std::error::Error>> {
    use ip::{add, Entry};
    let entry: Entry = submission.submission.into();
    add(&entry)?;
    Ok("".to_owned())
}

fn show_get_info(entry: &ip::Entry) -> slack::AttachedMessage {
    use slack::*;
    let mut m = AttachedMessage {
        attachments: vec![],
    };
    let mut a = Attachment { fields: vec![] };

    a.fields.push(AttachmentFields {
        title: "IP".to_owned(),
        value: entry.ip.clone(),
    });
    if let Some(ref domain) = entry.domain {
        a.fields.push(AttachmentFields {
            title: "도메인".to_owned(),
            value: domain.clone(),
        });
    }
    a.fields.push(AttachmentFields {
        title: "사용 여부".to_owned(),
        value: if entry.using {
            "사용중"
        } else {
            "미사용"
        }.to_owned(),
    });

    if !entry.open_ports.is_empty() {
        let mut s = String::new();
        for p in &entry.open_ports {
            s.push_str(&format!("{}, ", p));
        }
        s.pop();
        s.pop();
        s.push('\n');
        a.fields.push(AttachmentFields {
            title: "개방 포트".to_owned(),
            value: s,
        });
    }
    if let Some(ref description) = entry.description {
        a.fields.push(AttachmentFields {
            title: "설명".to_owned(),
            value: description.clone(),
        });
    }
    m.attachments.push(a);
    m
}

fn generate_ip_text(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, TextElement};
    Element::Text(TextElement {
        label: "IP".to_owned(),
        name: "ip".to_owned(),
        optional: None,
        hint: None,
        subtype: None,
        value,
        placeholder: None,
    })
}

fn generate_domain_text(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, TextElement};
    Element::Text(TextElement {
        label: "도메인".to_owned(),
        name: "domain".to_owned(),
        optional: Some(true),
        hint: None,
        subtype: None,
        value,
        placeholder: Some("Optional".to_owned()),
    })
}

fn generate_using_select(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, SelectElement, SelectOption};
    Element::Select(SelectElement {
        label: "사용 여부".to_owned(),
        name: "using".to_owned(),
        optional: None,
        options: vec![
            SelectOption {
                label: "사용중".to_owned(),
                value: "true".to_owned(),
            },
            SelectOption {
                label: "미사용".to_owned(),
                value: "false".to_owned(),
            },
        ],
        value,
        placeholder: None,
    })
}

fn generate_open_ports_text(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, TextElement};
    Element::Text(TextElement {
        label: "개방된 포트".to_owned(),
        name: "open_ports".to_owned(),
        optional: Some(true),
        hint: Some("쉼표로 구분".to_owned()),
        subtype: None,
        value,
        placeholder: Some("ex) 22, 80".to_owned()),
    })
}

fn generate_description_textarea(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, TextAreaElement};
    Element::TextArea(TextAreaElement {
        label: "설명".to_owned(),
        name: "description".to_owned(),
        optional: Some(true),
        hint: None,
        subtype: None,
        value,
        placeholder: Some("Optional".to_owned()),
    })
}
<commit_msg>edit command and submission<commit_after>#![feature(plugin, custom_derive, decl_macro)]
#![plugin(rocket_codegen)]
extern crate rocket;
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;

pub mod ip;

pub mod slack;

pub mod settings {
    extern crate config;

    #[derive(Deserialize)]
    pub struct Settings {
        pub verification_token: String,
        pub api_token: String,
    }

    impl Settings {
        pub fn new() -> Result<Settings, config::ConfigError> {
            let mut settings = config::Config::new();
            settings.merge(config::File::with_name("settings"))?;
            settings.try_into::<Settings>()
        }
    }
}

pub enum Response {
    PlainText(String),
    Dialog(slack::dialog::Dialog),
    AttachedMessage(slack::AttachedMessage),
    Json(serde_json::Value),
}

pub fn handle_command(
    settings: &settings::Settings,
    command: &str,
    data: slack::slash_command::Request,
) -> Result<serde_json::Value, Box<std::error::Error>> {
    if settings.verification_token != data.token {
        return Ok(json!({"text": "Validation error".to_owned()}));
    }

    let result = match command {
        "add" => add_command(),
        "get" => get_command(&data.text),
        "edit" => edit_command(&data.text),
        _ => Err(From::from(format!("No such command: {}", command))),
    }?;

    match result {
        Response::PlainText(t) => Ok(json!({ "text": t })),
        Response::Dialog(d) => {
            show_dialog(&settings.api_token, d, &data.trigger_id)?;
            Ok(json!({ "text": "Dialog opened!" }))
        }
        Response::AttachedMessage(m) => Ok(serde_json::to_value(m)?),
        Response::Json(j) => Ok(j),
    }
}

pub fn handle_submission(
    settings: &settings::Settings,
    submission: slack::dialog::Submission,
) -> Result<String, Box<std::error::Error>> {
    if settings.verification_token != submission.token {
        return Ok("Validation error".to_owned());
    }

    match submission.callback_id.as_ref() {
        "add" => add_submission(submission),
        "edit" => edit_submission(submission),
        _ => Err(From::from(
            format!("No such submission: {}", submission.callback_id),
        )),
    }
}

fn add_command() -> Result<Response, Box<std::error::Error>> {
    Ok(Response::Dialog(generate_add_dialog()))
}

fn get_command(query: &str) -> Result<Response, Box<std::error::Error>> {
    use ip::get;
    if query.is_empty() {
        return Ok(Response::PlainText("Validation error".to_owned()));
    }
    let entry = get(query);
    match entry {
        Some(e) => Ok(Response::AttachedMessage(generate_get_message(e))),
        None => Ok(Response::PlainText("IP not found".to_owned())),
    }
}

fn edit_command(query: &str) -> Result<Response, Box<std::error::Error>> {
    use ip::get;
    if query.is_empty() {
        return Ok(Response::PlainText("Validation error".to_owned()));
    }
    let entry = match get(query) {
        None => return Ok(Response::PlainText("IP not found".to_owned())),
        Some(e) => e,
    };

    Ok(Response::Dialog(generate_edit_dialog(entry)))
}

fn add_submission(submission: slack::dialog::Submission) -> Result<String, Box<std::error::Error>> {
    use ip::{add, Entry};
    let entry: Entry = submission.submission.into();
    add(&entry)?;
    Ok(format!("IP {} added!", entry.ip))
}

fn edit_submission(
    submission: slack::dialog::Submission,
) -> Result<String, Box<std::error::Error>> {
    use ip::{add, Entry};
    let entry: Entry = submission.submission.into();
    add(&entry)?;
    Ok(format!("IP {} added!", entry.ip))
}

fn generate_get_message(entry: ip::Entry) -> slack::AttachedMessage {
    use slack::*;
    let mut m = AttachedMessage {
        attachments: vec![],
    };
    let mut a = Attachment { fields: vec![] };

    a.fields.push(AttachmentFields {
        title: "IP".to_owned(),
        value: entry.ip,
    });
    if let Some(domain) = entry.domain {
        a.fields.push(AttachmentFields {
            title: "도메인".to_owned(),
            value: domain,
        });
    }
    a.fields.push(AttachmentFields {
        title: "사용 여부".to_owned(),
        value: if entry.using {
            "사용중"
        } else {
            "미사용"
        }.to_owned(),
    });

    if !entry.open_ports.is_empty() {
        let mut s = String::new();
        for p in entry.open_ports {
            s.push_str(&format!("{}, ", p));
        }
        s.pop();
        s.pop();
        a.fields.push(AttachmentFields {
            title: "개방 포트".to_owned(),
            value: s,
        });
    }
    if let Some(description) = entry.description {
        a.fields.push(AttachmentFields {
            title: "설명".to_owned(),
            value: description,
        });
    }
    m.attachments.push(a);
    m
}

fn show_dialog(
    token: &str,
    dialog: slack::dialog::Dialog,
    trigger_id: &str,
) -> Result<(), Box<std::error::Error>> {
    let request = slack::dialog::OpenRequest {
        token: token.to_owned(),
        dialog,
        trigger_id: trigger_id.to_owned(),
    };
    slack::dialog::open(request)?;
    Ok(())
}

fn generate_add_dialog() -> slack::dialog::Dialog {
    let mut dialog = slack::dialog::Dialog::new("add".to_owned(), "IP 추가".to_owned());

    dialog
        .elements
        .push(generate_ip_text(None).into_json().unwrap());
    dialog
        .elements
        .push(generate_domain_text(None).into_json().unwrap());
    dialog
        .elements
        .push(generate_using_select(None).into_json().unwrap());
    dialog
        .elements
        .push(generate_open_ports_text(None).into_json().unwrap());
    dialog
        .elements
        .push(generate_description_textarea(None).into_json().unwrap());

    dialog
}

fn generate_edit_dialog(entry: ip::Entry) -> slack::dialog::Dialog {
    let mut dialog = slack::dialog::Dialog::new("edit".to_owned(), "IP 수정".to_owned());

    dialog
        .elements
        .push(generate_ip_text(Some(entry.ip)).into_json().unwrap());
    dialog
        .elements
        .push(generate_domain_text(entry.domain).into_json().unwrap());
    dialog.elements.push(
        generate_using_select(Some(if entry.using { "true" } else { "false" }.to_owned()))
            .into_json()
            .unwrap(),
    );
    dialog.elements.push(
        generate_open_ports_text({
            if !entry.open_ports.is_empty() {
                let mut s = String::new();
                for p in entry.open_ports {
                    s.push_str(&format!("{}, ", p));
                }
                s.pop();
                s.pop();
                Some(s)
            } else {
                None
            }
        }).into_json()
            .unwrap(),
    );
    dialog.elements.push(
        generate_description_textarea(entry.description)
            .into_json()
            .unwrap(),
    );

    dialog
}

fn generate_ip_text(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, TextElement};
    Element::Text(TextElement {
        label: "IP".to_owned(),
        name: "ip".to_owned(),
        optional: None,
        hint: None,
        subtype: None,
        value,
        placeholder: None,
    })
}

fn generate_domain_text(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, TextElement};
    Element::Text(TextElement {
        label: "도메인".to_owned(),
        name: "domain".to_owned(),
        optional: Some(true),
        hint: None,
        subtype: None,
        value,
        placeholder: Some("Optional".to_owned()),
    })
}

fn generate_using_select(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, SelectElement, SelectOption};
    Element::Select(SelectElement {
        label: "사용 여부".to_owned(),
        name: "using".to_owned(),
        optional: None,
        options: vec![
            SelectOption {
                label: "사용중".to_owned(),
                value: "true".to_owned(),
            },
            SelectOption {
                label: "미사용".to_owned(),
                value: "false".to_owned(),
            },
        ],
        value,
        placeholder: None,
    })
}

fn generate_open_ports_text(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, TextElement};
    Element::Text(TextElement {
        label: "개방된 포트".to_owned(),
        name: "open_ports".to_owned(),
        optional: Some(true),
        hint: Some("쉼표로 구분".to_owned()),
        subtype: None,
        value,
        placeholder: Some("ex) 22, 80".to_owned()),
    })
}

fn generate_description_textarea(value: Option<String>) -> slack::dialog::element::Element {
    use slack::dialog::element::{Element, TextAreaElement};
    Element::TextArea(TextAreaElement {
        label: "설명".to_owned(),
        name: "description".to_owned(),
        optional: Some(true),
        hint: None,
        subtype: None,
        value,
        placeholder: Some("Optional".to_owned()),
    })
}
<|endoftext|>


<commit_before>extern crate redis;

use std::time::Duration;

use redis::{Connection, RedisError, cmd};

fn duration_to_millis(d: &Duration) -> u64 {
    (d.subsec_nanos() / 1_000_000) as u64 + d.as_secs()
}

macro_rules! option_arg {
    ($disque: expr, $name: expr, $param: expr) => (
        match $param {
            Some(u) => $disque.arg($name).arg(u),
            None => &mut $disque,
        };
    )
}
pub struct Disque {
    connection: Connection,
}

impl Disque {
    pub fn open(url: &str) -> Result<Disque, RedisError> {
        let client = try!(redis::Client::open(url));
        let connection = try!(client.get_connection());
        Ok(Disque { connection: connection })
    }

    pub fn hello(&self) -> Result<Vec<Vec<u8>>, RedisError> {
        cmd("HELLO").query(&self.connection)
    }

    pub fn addjob(&self, queue_name: &[u8], job: &[u8], timeout: Duration,
            replicate: Option<usize>, delay: Option<Duration>,
            retry: Option<Duration>, ttl: Option<Duration>,
            maxlen: Option<usize>, async: bool,
            ) -> Result<String, RedisError> {
        let mut c = cmd("ADDJOB");
        c
            .arg(queue_name)
            .arg(job)
            .arg(duration_to_millis(&timeout));
        option_arg!(c, "REPLICATE", replicate);
        option_arg!(c, "DELAY", delay.map(|x| x.as_secs()));
        option_arg!(c, "RETRY", retry.map(|x| x.as_secs()));
        option_arg!(c, "TTL", ttl.map(|x| x.as_secs()));
        option_arg!(c, "MAXLEN", maxlen);
        if async { c.arg("ASYNC"); }
        c.query(&self.connection)
    }

    pub fn getjob_count(&self, nohang: bool, timeout: Option<Duration>,
            count: usize, withcounters: bool, queues: &[&[u8]]
            ) -> Result<Vec<Vec<Vec<u8>>>, RedisError> {
        let mut c = cmd("GETJOB");
        if nohang { c.arg("NOHANG"); }
        option_arg!(c, "TIMEOUT", timeout.map(|t| duration_to_millis(&t)));
        c.arg("COUNT").arg(count);
        if withcounters { c.arg("WITHCOUNTERS"); }
        c.arg("FROM");
        for queue in queues { c.arg(*queue); }
        c.query(&self.connection)
    }

    pub fn getjob(&self, nohang: bool, timeout: Option<Duration>,
            withcounters: bool, queues: &[&[u8]]
            ) -> Result<Option<Vec<Vec<u8>>>, RedisError> {
        let mut jobs = try!(self.getjob_count(nohang, timeout, 1, withcounters,
                    queues));
        Ok(jobs.pop())
    }

    pub fn ackjob(&self, jobids: &[&[u8]]) -> Result<bool, RedisError> {
        let mut c = cmd("ACKJOB");
        for jobid in jobids { c.arg(*jobid); }
        c.query(&self.connection)
    }
}

#[cfg(test)]
fn conn() -> Disque {
    Disque::open("redis://127.0.0.1:7711/").unwrap()
}

#[test]
fn can_connect() {
    conn();
}

#[test]
fn hello() {
    let disque = conn();
    disque.hello().unwrap();
}

#[test]
fn addjob() {
    let disque = conn();
    let jobid = disque.addjob(b"queue", b"job", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    assert_eq!(jobid.len(), 48);
    assert_eq!(&jobid[..2], "DI");
    assert_eq!(&jobid[46..], "SQ");
}

#[test]
fn getjob_count() {
    let disque = conn();
    let j1 = disque.addjob(b"queue1", b"job1", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    let j2 = disque.addjob(b"queue2", b"job2", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    disque.addjob(b"queue3", b"job3", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    let jobs = disque.getjob_count(false, None, 3, true, &[b"queue1", b"queue2"]).unwrap();
    assert_eq!(jobs.len(), 2);
    assert_eq!(jobs[0][0], b"queue1");
    assert_eq!(jobs[0][1], j1.into_bytes());
    assert_eq!(jobs[0][2], b"job1");
    assert_eq!(jobs[1][0], b"queue2");
    assert_eq!(jobs[1][1], j2.into_bytes());
    assert_eq!(jobs[1][2], b"job2");
}

#[test]
fn getjob() {
    let disque = conn();
    let jobid = disque.addjob(b"queue4", b"job4", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    let job = disque.getjob(false, None, true, &[b"queue4", b"queue5"]).unwrap().unwrap();
    assert_eq!(job[0], b"queue4");
    assert_eq!(job[1], jobid.into_bytes());
    assert_eq!(job[2], b"job4");
}

#[test]
fn ackjob() {
    let disque = conn();
    let jobid = disque.addjob(b"queue6", b"job6", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    assert!(disque.ackjob(&[jobid.as_bytes()]).unwrap());
    assert!(!disque.ackjob(&[jobid.as_bytes()]).unwrap());
    assert!(!disque.ackjob(&[jobid.as_bytes()]).unwrap());
}
<commit_msg>Add FASTACK<commit_after>extern crate redis;

use std::time::Duration;

use redis::{Connection, RedisError, cmd};

fn duration_to_millis(d: &Duration) -> u64 {
    (d.subsec_nanos() / 1_000_000) as u64 + d.as_secs()
}

macro_rules! option_arg {
    ($disque: expr, $name: expr, $param: expr) => (
        match $param {
            Some(u) => $disque.arg($name).arg(u),
            None => &mut $disque,
        };
    )
}
pub struct Disque {
    connection: Connection,
}

impl Disque {
    pub fn open(url: &str) -> Result<Disque, RedisError> {
        let client = try!(redis::Client::open(url));
        let connection = try!(client.get_connection());
        Ok(Disque { connection: connection })
    }

    pub fn hello(&self) -> Result<Vec<Vec<u8>>, RedisError> {
        cmd("HELLO").query(&self.connection)
    }

    pub fn addjob(&self, queue_name: &[u8], job: &[u8], timeout: Duration,
            replicate: Option<usize>, delay: Option<Duration>,
            retry: Option<Duration>, ttl: Option<Duration>,
            maxlen: Option<usize>, async: bool,
            ) -> Result<String, RedisError> {
        let mut c = cmd("ADDJOB");
        c
            .arg(queue_name)
            .arg(job)
            .arg(duration_to_millis(&timeout));
        option_arg!(c, "REPLICATE", replicate);
        option_arg!(c, "DELAY", delay.map(|x| x.as_secs()));
        option_arg!(c, "RETRY", retry.map(|x| x.as_secs()));
        option_arg!(c, "TTL", ttl.map(|x| x.as_secs()));
        option_arg!(c, "MAXLEN", maxlen);
        if async { c.arg("ASYNC"); }
        c.query(&self.connection)
    }

    pub fn getjob_count(&self, nohang: bool, timeout: Option<Duration>,
            count: usize, withcounters: bool, queues: &[&[u8]]
            ) -> Result<Vec<Vec<Vec<u8>>>, RedisError> {
        let mut c = cmd("GETJOB");
        if nohang { c.arg("NOHANG"); }
        option_arg!(c, "TIMEOUT", timeout.map(|t| duration_to_millis(&t)));
        c.arg("COUNT").arg(count);
        if withcounters { c.arg("WITHCOUNTERS"); }
        c.arg("FROM");
        for queue in queues { c.arg(*queue); }
        c.query(&self.connection)
    }

    pub fn getjob(&self, nohang: bool, timeout: Option<Duration>,
            withcounters: bool, queues: &[&[u8]]
            ) -> Result<Option<Vec<Vec<u8>>>, RedisError> {
        let mut jobs = try!(self.getjob_count(nohang, timeout, 1, withcounters,
                    queues));
        Ok(jobs.pop())
    }

    pub fn ackjob(&self, jobids: &[&[u8]]) -> Result<bool, RedisError> {
        let mut c = cmd("ACKJOB");
        for jobid in jobids { c.arg(*jobid); }
        c.query(&self.connection)
    }

    pub fn fastack(&self, jobids: &[&[u8]]) -> Result<bool, RedisError> {
        let mut c = cmd("FASTACK");
        for jobid in jobids { c.arg(*jobid); }
        c.query(&self.connection)
    }
}

#[cfg(test)]
fn conn() -> Disque {
    Disque::open("redis://127.0.0.1:7711/").unwrap()
}

#[test]
fn can_connect() {
    conn();
}

#[test]
fn hello() {
    let disque = conn();
    disque.hello().unwrap();
}

#[test]
fn addjob() {
    let disque = conn();
    let jobid = disque.addjob(b"queue", b"job", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    assert_eq!(jobid.len(), 48);
    assert_eq!(&jobid[..2], "DI");
    assert_eq!(&jobid[46..], "SQ");
}

#[test]
fn getjob_count() {
    let disque = conn();
    let j1 = disque.addjob(b"queue1", b"job1", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    let j2 = disque.addjob(b"queue2", b"job2", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    disque.addjob(b"queue3", b"job3", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    let jobs = disque.getjob_count(false, None, 3, true, &[b"queue1", b"queue2"]).unwrap();
    assert_eq!(jobs.len(), 2);
    assert_eq!(jobs[0][0], b"queue1");
    assert_eq!(jobs[0][1], j1.into_bytes());
    assert_eq!(jobs[0][2], b"job1");
    assert_eq!(jobs[1][0], b"queue2");
    assert_eq!(jobs[1][1], j2.into_bytes());
    assert_eq!(jobs[1][2], b"job2");
}

#[test]
fn getjob() {
    let disque = conn();
    let jobid = disque.addjob(b"queue4", b"job4", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    let job = disque.getjob(false, None, true, &[b"queue4", b"queue5"]).unwrap().unwrap();
    assert_eq!(job[0], b"queue4");
    assert_eq!(job[1], jobid.into_bytes());
    assert_eq!(job[2], b"job4");
}

#[test]
fn ackjob() {
    let disque = conn();
    let jobid = disque.addjob(b"queue6", b"job6", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    assert!(disque.ackjob(&[jobid.as_bytes()]).unwrap());
    assert!(!disque.ackjob(&[jobid.as_bytes()]).unwrap());
    assert!(!disque.ackjob(&[jobid.as_bytes()]).unwrap());
}

#[test]
fn fastack() {
    let disque = conn();
    let jobid = disque.addjob(b"queue7", b"job7", Duration::from_secs(10), None, None, None, None, None, false).unwrap();
    assert!(disque.fastack(&[jobid.as_bytes()]).unwrap());
    assert!(!disque.fastack(&[jobid.as_bytes()]).unwrap());
    assert!(!disque.fastack(&[jobid.as_bytes()]).unwrap());
}
<|endoftext|>


<commit_before>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! # Rust-bio, a bioinformatics library for Rust.
//! This library provides implementations of many algorithms and data structures
//! that are useful for bioinformatics.
//! All provided implementations are rigorously tested via continuous
//! integration.
//! For installation instructions and a general overview, visit
//! https://rust-bio.github.io.
//!
//! Currently, rust-bio provides
//!
//! * most major pattern matching algorithms,
//! * a convenient alphabet implementation,
//! * pairwise alignment,
//! * suffix arrays,
//! * BWT and FM-Index,
//! * FMD-Index for finding supermaximal exact matches,
//! * a q-gram index,
//! * an orf research algorithm,
//! * a rank/select data structure,
//! * FASTQ and FASTA and BED readers and writers,
//! * helper functions for combinatorics and dealing with log probabilities.
//!
//! # Example
//!
//! ```rust
//! use bio::alphabets;
//! use bio::data_structures::suffix_array::suffix_array;
//! use bio::data_structures::bwt::{bwt, less, Occ};
//! use bio::data_structures::fmindex::{FMIndex, FMIndexable};
//! use std::rc::Rc;
//!
//! let text = b"ACGGATGCTGGATCGGATCGCGCTAGCTA$";
//! let pattern = b"ACCG";
//!
//! // Create an FM-Index for a given text.
//! let alphabet = alphabets::dna::iupac_alphabet();
//! let sa = suffix_array(text);
//! let bwt = Rc::new(bwt(text, &sa));
//! let less = Rc::new(less(&bwt, &alphabet));
//! let occ = Rc::new(Occ::new(&bwt, 3, &alphabet));
//! let fmindex = FMIndex::new(bwt, less, occ);
//!
//! let interval = fmindex.backward_search(pattern.iter());
//! let positions = interval.lower..interval.upper;
//! ```
//!
//! Documentation and further examples for each module can be found in the module descriptions below.

#![cfg_attr(feature = "serde_macros", feature(const_fn, custom_derive, plugin))]
#![cfg_attr(feature = "serde_macros", plugin(serde_macros))]

#[cfg(feature = "serde_macros")]
extern crate serde;

extern crate rustc_serialize;
extern crate csv;
extern crate num;
extern crate itertools;
extern crate bit_vec;
extern crate vec_map;
extern crate bit_set;
#[macro_use]
extern crate lazy_static;
extern crate nalgebra;
#[macro_use]
extern crate approx;

pub mod utils;
pub mod alphabets;
pub mod pattern_matching;
pub mod data_structures;
pub mod alignment;
pub mod io;
pub mod seq_analysis;
pub mod stats;
pub mod scores;
<commit_msg>Updated doctest to use Interval::occ<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! # Rust-bio, a bioinformatics library for Rust.
//! This library provides implementations of many algorithms and data structures
//! that are useful for bioinformatics.
//! All provided implementations are rigorously tested via continuous
//! integration.
//! For installation instructions and a general overview, visit
//! https://rust-bio.github.io.
//!
//! Currently, rust-bio provides
//!
//! * most major pattern matching algorithms,
//! * a convenient alphabet implementation,
//! * pairwise alignment,
//! * suffix arrays,
//! * BWT and FM-Index,
//! * FMD-Index for finding supermaximal exact matches,
//! * a q-gram index,
//! * an orf research algorithm,
//! * a rank/select data structure,
//! * FASTQ and FASTA and BED readers and writers,
//! * helper functions for combinatorics and dealing with log probabilities.
//!
//! # Example
//!
//! ```rust
//! use bio::alphabets;
//! use bio::data_structures::suffix_array::suffix_array;
//! use bio::data_structures::bwt::{bwt, less, Occ};
//! use bio::data_structures::fmindex::{FMIndex, FMIndexable};
//! use std::rc::Rc;
//!
//! let text = b"ACGGATGCTGGATCGGATCGCGCTAGCTA$";
//! let pattern = b"ACCG";
//!
//! // Create an FM-Index for a given text.
//! let alphabet = alphabets::dna::iupac_alphabet();
//! let sa = suffix_array(text);
//! let bwt = Rc::new(bwt(text, &sa));
//! let less = Rc::new(less(&bwt, &alphabet));
//! let occ = Rc::new(Occ::new(&bwt, 3, &alphabet));
//! let fmindex = FMIndex::new(bwt, less, occ);
//!
//! let interval = fmindex.backward_search(pattern.iter());
//! let positions = interval.occ(&sa);
//! ```
//!
//! Documentation and further examples for each module can be found in the module descriptions below.

#![cfg_attr(feature = "serde_macros", feature(const_fn, custom_derive, plugin))]
#![cfg_attr(feature = "serde_macros", plugin(serde_macros))]

#[cfg(feature = "serde_macros")]
extern crate serde;

extern crate rustc_serialize;
extern crate csv;
extern crate num;
extern crate itertools;
extern crate bit_vec;
extern crate vec_map;
extern crate bit_set;
#[macro_use]
extern crate lazy_static;
extern crate nalgebra;
#[macro_use]
extern crate approx;

pub mod utils;
pub mod alphabets;
pub mod pattern_matching;
pub mod data_structures;
pub mod alignment;
pub mod io;
pub mod seq_analysis;
pub mod stats;
pub mod scores;
<|endoftext|>


<commit_before>//! Crate **ruma_identifiers** contains types for [Matrix](https://matrix.org/) opaque identifiers,
//! such as user IDs, room IDs, and room aliases.

#![deny(missing_docs)]

#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate url;

use std::fmt::{Display, Formatter, Result as FmtResult};

use regex::Regex;
use url::{Host, ParseError, Url};

/// All events must be 255 bytes or less.
const MAX_BYTES: usize = 255;
/// The minimum number of characters an ID can be.
///
/// This is an optimization and not required by the spec. The shortest possible valid ID is a sigil
/// + a single character local ID + a colon + a single character hostname.
const MIN_CHARS: usize = 4;
/// The number of bytes in a valid sigil.
const SIGIL_BYTES: usize = 1;

lazy_static! {
    static ref USER_LOCALPART_PATTERN: Regex =
        Regex::new(r"\A[a-z0-9._=-]+\z").expect("Failed to create user localpart regex.");
}

/// An error encountered when trying to parse an invalid user ID string.
#[derive(Debug, PartialEq)]
pub enum Error {
    /// The ID's localpart contains invalid characters.
    InvalidCharacters,
    /// The domain part of the user ID string was not a valid IP address or DNS name.
    InvalidHost,
    /// The ID exceeds 255 bytes.
    MaximumLengthExceeded,
    /// The ID is less than 4 characters.
    MinimumLengthNotSatisfied,
    /// The ID is missing the colon delimiter between localpart and server name.
    MissingDelimiter,
    /// The ID is missing the leading sigil.
    MissingSigil,
}

/// A Matrix user ID.
///
/// A `UserId` is created from a string slice, and can be converted back into a string as needed:
///
/// ```
/// # use ruma_identifiers::UserId;
/// assert_eq!(UserId::new("@carl:example.com").unwrap().to_string(), "@carl:example.com");
/// ```
#[derive(Debug)]
pub struct UserId {
    hostname: Host,
    localpart: String,
    port: u16,
}

fn parse_id<'a>(required_sigil: char, id: &'a str) -> Result<(&'a str, Host, u16), Error> {
    if id.len() > MAX_BYTES {
        return Err(Error::MaximumLengthExceeded);
    }

    let mut chars = id.chars();

    if id.len() < MIN_CHARS {
        return Err(Error::MinimumLengthNotSatisfied);
    }

    let sigil = chars.nth(0).expect("ID missing first character.");

    if sigil != required_sigil {
        return Err(Error::MissingSigil);
    }

    let delimiter_index = match chars.position(|c| c == ':') {
        Some(index) => index + 1,
        None => return Err(Error::MissingDelimiter),
    };

    let localpart = &id[1..delimiter_index];
    let raw_host = &id[delimiter_index + SIGIL_BYTES..];
    let url_string = format!("https://{}", raw_host);
    let url = try!(Url::parse(&url_string));

    let host = match url.host() {
        Some(host) => host.to_owned(),
        None => return Err(Error::InvalidHost),
    };

    let port = url.port().unwrap_or(443);

    Ok((localpart, host, port))
}

impl UserId {
    /// Create a new Matrix user ID from a string representation.
    ///
    /// The string must include the leading @ sigil, the localpart, a literal colon, and a valid
    /// server name.
    pub fn new(user_id: &str) -> Result<UserId, Error> {
        let (localpart, host, port) = try!(parse_id('@', user_id));

        if !USER_LOCALPART_PATTERN.is_match(localpart) {
            return Err(Error::InvalidCharacters);
        }

        Ok(UserId {
            hostname: host,
            port: port,
            localpart: localpart.to_owned(),
        })
    }

    /// Returns a `url::Host` for the user ID, containing the server name (minus the port) of the
    /// user's homeserver.
    ///
    /// This host can be either a domain name, an IPv4 address, or an IPv6 address.
    pub fn hostname(&self) -> &Host {
        &self.hostname
    }

    /// Returns the user's localpart.
    pub fn localpart(&self) -> &str {
        &self.localpart
    }

    /// Returns the port the user's homeserver can be accessed on.
    pub fn port(&self) -> u16 {
        self.port
    }
}

impl From<ParseError> for Error {
    fn from(_: ParseError) -> Error {
        Error::InvalidHost
    }
}

impl Display for UserId {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        if self.port == 443 {
            write!(f, "@{}:{}", self.localpart, self.hostname)
        } else {
            write!(f, "@{}:{}:{}", self.localpart, self.hostname, self.port)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Error, UserId};

    #[test]
    fn valid_user_id() {
        assert_eq!(
            UserId::new("@carl:example.com")
                .expect("Failed to create UserId.")
                .to_string(),
            "@carl:example.com"
        );
    }

    #[test]
    fn valid_user_id_with_explicit_standard_port() {
        assert_eq!(
            UserId::new("@carl:example.com:443")
                .expect("Failed to create UserId.")
                .to_string(),
            "@carl:example.com"
        );
    }

    #[test]
    fn valid_user_id_with_non_standard_port() {
        assert_eq!(
            UserId::new("@carl:example.com:5000")
                .expect("Failed to create UserId.")
                .to_string(),
            "@carl:example.com:5000"
        );
    }

    #[test]
    fn invalid_characters_in_localpart() {
        assert_eq!(
            UserId::new("@CARL:example.com").err().unwrap(),
            Error::InvalidCharacters
        );
    }

    #[test]
    fn missing_sigil() {
        assert_eq!(
            UserId::new("carl:example.com").err().unwrap(),
            Error::MissingSigil
        );
    }

    #[test]
    fn missing_delimiter() {
        assert_eq!(
            UserId::new("@carl").err().unwrap(),
            Error::MissingDelimiter
        );
    }

    #[test]
    fn invalid_host() {
        assert_eq!(
            UserId::new("@carl:-").err().unwrap(),
            Error::InvalidHost
        );
    }

    #[test]
    fn invalid_port() {
        assert_eq!(
            UserId::new("@carl:example.com:notaport").err().unwrap(),
            Error::InvalidHost
        );
    }
}
<commit_msg>Add event and room IDs.<commit_after>//! Crate **ruma_identifiers** contains types for [Matrix](https://matrix.org/) opaque identifiers,
//! such as user IDs, room IDs, and room aliases.

#![deny(missing_docs)]

#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate url;

use std::fmt::{Display, Formatter, Result as FmtResult};

use regex::Regex;
use url::{Host, ParseError, Url};

/// All events must be 255 bytes or less.
const MAX_BYTES: usize = 255;
/// The minimum number of characters an ID can be.
///
/// This is an optimization and not required by the spec. The shortest possible valid ID is a sigil
/// + a single character local ID + a colon + a single character hostname.
const MIN_CHARS: usize = 4;
/// The number of bytes in a valid sigil.
const SIGIL_BYTES: usize = 1;

lazy_static! {
    static ref USER_LOCALPART_PATTERN: Regex =
        Regex::new(r"\A[a-z0-9._=-]+\z").expect("Failed to create user localpart regex.");
}

/// An error encountered when trying to parse an invalid user ID string.
#[derive(Debug, PartialEq)]
pub enum Error {
    /// The ID's localpart contains invalid characters.
    InvalidCharacters,
    /// The domain part of the user ID string was not a valid IP address or DNS name.
    InvalidHost,
    /// The ID exceeds 255 bytes.
    MaximumLengthExceeded,
    /// The ID is less than 4 characters.
    MinimumLengthNotSatisfied,
    /// The ID is missing the colon delimiter between localpart and server name.
    MissingDelimiter,
    /// The ID is missing the leading sigil.
    MissingSigil,
}

/// A Matrix event ID.
///
/// An `EventId` is created from a string slice, and can be converted back into a string as needed:
///
/// ```
/// # use ruma_identifiers::EventId;
/// assert_eq!(EventId::new("$h29iv0s8:example.com").unwrap().to_string(), "$h29iv0s8:example.com");
/// ```
#[derive(Debug)]
pub struct EventId {
    hostname: Host,
    opaque_id: String,
    port: u16,
}

/// A Matrix room ID.
///
/// An `RoomId` is created from a string slice, and can be converted back into a string as needed:
///
/// ```
/// # use ruma_identifiers::RoomId;
/// assert_eq!(RoomId::new("!n8f893n9:example.com").unwrap().to_string(), "!n8f893n9:example.com");
/// ```
#[derive(Debug)]
pub struct RoomId {
    hostname: Host,
    opaque_id: String,
    port: u16,
}

/// A Matrix user ID.
///
/// A `UserId` is created from a string slice, and can be converted back into a string as needed:
///
/// ```
/// # use ruma_identifiers::UserId;
/// assert_eq!(UserId::new("@carl:example.com").unwrap().to_string(), "@carl:example.com");
/// ```
#[derive(Debug)]
pub struct UserId {
    hostname: Host,
    localpart: String,
    port: u16,
}

fn display(f: &mut Formatter, sigil: char, localpart: &str, hostname: &Host, port: u16)
-> FmtResult {
    if port == 443 {
        write!(f, "{}{}:{}", sigil, localpart, hostname)
    } else {
        write!(f, "{}{}:{}:{}", sigil, localpart, hostname, port)
    }
}

fn parse_id<'a>(required_sigil: char, id: &'a str) -> Result<(&'a str, Host, u16), Error> {
    if id.len() > MAX_BYTES {
        return Err(Error::MaximumLengthExceeded);
    }

    let mut chars = id.chars();

    if id.len() < MIN_CHARS {
        return Err(Error::MinimumLengthNotSatisfied);
    }

    let sigil = chars.nth(0).expect("ID missing first character.");

    if sigil != required_sigil {
        return Err(Error::MissingSigil);
    }

    let delimiter_index = match chars.position(|c| c == ':') {
        Some(index) => index + 1,
        None => return Err(Error::MissingDelimiter),
    };

    let localpart = &id[1..delimiter_index];
    let raw_host = &id[delimiter_index + SIGIL_BYTES..];
    let url_string = format!("https://{}", raw_host);
    let url = try!(Url::parse(&url_string));

    let host = match url.host() {
        Some(host) => host.to_owned(),
        None => return Err(Error::InvalidHost),
    };

    let port = url.port().unwrap_or(443);

    Ok((localpart, host, port))
}

impl EventId {
    /// Creates a new Matrix event ID from a string representation.
    ///
    /// The string must include the leading $ sigil, the opaque ID, a literal colon, and a valid
    /// server name.
    pub fn new(event_id: &str) -> Result<Self, Error> {
        let (opaque_id, host, port) = try!(parse_id('$', event_id));

        Ok(EventId {
            hostname: host,
            opaque_id: opaque_id.to_owned(),
            port: port,
        })
    }

    /// Returns a `url::Host` for the event ID, containing the server name (minus the port) of the
    /// originating homeserver.
    ///
    /// The host can be either a domain name, an IPv4 address, or an IPv6 address.
    pub fn hostname(&self) -> &Host {
        &self.hostname
    }

    /// Returns the event's opaque ID.
    pub fn opaque_id(&self) -> &str {
        &self.opaque_id
    }

    /// Returns the port the originating homeserver can be accessed on.
    pub fn port(&self) -> u16 {
        self.port
    }
}

impl RoomId {
    /// Creates a new Matrix room ID from a string representation.
    ///
    /// The string must include the leading ! sigil, the opaque ID, a literal colon, and a valid
    /// server name.
    pub fn new(room_id: &str) -> Result<Self, Error> {
        let (opaque_id, host, port) = try!(parse_id('!', room_id));

        Ok(RoomId {
            hostname: host,
            opaque_id: opaque_id.to_owned(),
            port: port,
        })
    }

    /// Returns a `url::Host` for the room ID, containing the server name (minus the port) of the
    /// originating homeserver.
    ///
    /// The host can be either a domain name, an IPv4 address, or an IPv6 address.
    pub fn hostname(&self) -> &Host {
        &self.hostname
    }

    /// Returns the event's opaque ID.
    pub fn opaque_id(&self) -> &str {
        &self.opaque_id
    }

    /// Returns the port the originating homeserver can be accessed on.
    pub fn port(&self) -> u16 {
        self.port
    }
}

impl UserId {
    /// Creates a new Matrix user ID from a string representation.
    ///
    /// The string must include the leading @ sigil, the localpart, a literal colon, and a valid
    /// server name.
    pub fn new(user_id: &str) -> Result<UserId, Error> {
        let (localpart, host, port) = try!(parse_id('@', user_id));

        if !USER_LOCALPART_PATTERN.is_match(localpart) {
            return Err(Error::InvalidCharacters);
        }

        Ok(UserId {
            hostname: host,
            port: port,
            localpart: localpart.to_owned(),
        })
    }

    /// Returns a `url::Host` for the user ID, containing the server name (minus the port) of the
    /// originating homeserver.
    ///
    /// The host can be either a domain name, an IPv4 address, or an IPv6 address.
    pub fn hostname(&self) -> &Host {
        &self.hostname
    }

    /// Returns the user's localpart.
    pub fn localpart(&self) -> &str {
        &self.localpart
    }

    /// Returns the port the originating homeserver can be accessed on.
    pub fn port(&self) -> u16 {
        self.port
    }
}

impl From<ParseError> for Error {
    fn from(_: ParseError) -> Error {
        Error::InvalidHost
    }
}

impl Display for EventId {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        display(f, '$', &self.opaque_id, &self.hostname, self.port)
    }
}

impl Display for RoomId {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        display(f, '!', &self.opaque_id, &self.hostname, self.port)
    }
}

impl Display for UserId {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        display(f, '@', &self.localpart, &self.hostname, self.port)
    }
}

#[cfg(test)]
mod tests {
    use super::{Error, EventId, RoomId, UserId};

    #[test]
    fn valid_event_id() {
        assert_eq!(
            EventId::new("$39hvsi03hlne:example.com")
                .expect("Failed to create EventId.")
                .to_string(),
            "$39hvsi03hlne:example.com"
        );
    }

    #[test]
    fn valid_event_id_with_explicit_standard_port() {
        assert_eq!(
            EventId::new("$39hvsi03hlne:example.com:443")
                .expect("Failed to create EventId.")
                .to_string(),
            "$39hvsi03hlne:example.com"
        );
    }

    #[test]
    fn valid_event_id_with_non_standard_port() {
        assert_eq!(
            EventId::new("$39hvsi03hlne:example.com:5000")
                .expect("Failed to create EventId.")
                .to_string(),
            "$39hvsi03hlne:example.com:5000"
        );
    }

    #[test]
    fn missing_event_id_sigil() {
        assert_eq!(
            EventId::new("39hvsi03hlne:example.com").err().unwrap(),
            Error::MissingSigil
        );
    }

    #[test]
    fn missing_event_id_delimiter() {
        assert_eq!(
            EventId::new("$39hvsi03hlne").err().unwrap(),
            Error::MissingDelimiter
        );
    }

    #[test]
    fn invalid_event_id_host() {
        assert_eq!(
            EventId::new("$39hvsi03hlne:-").err().unwrap(),
            Error::InvalidHost
        );
    }

    #[test]
    fn invalid_event_id_port() {
        assert_eq!(
            EventId::new("$39hvsi03hlne:example.com:notaport").err().unwrap(),
            Error::InvalidHost
        );
    }

    #[test]
    fn valid_room_id() {
        assert_eq!(
            RoomId::new("!29fhd83h92h0:example.com")
                .expect("Failed to create RoomId.")
                .to_string(),
            "!29fhd83h92h0:example.com"
        );
    }

    #[test]
    fn valid_room_id_with_explicit_standard_port() {
        assert_eq!(
            RoomId::new("!29fhd83h92h0:example.com:443")
                .expect("Failed to create RoomId.")
                .to_string(),
            "!29fhd83h92h0:example.com"
        );
    }

    #[test]
    fn valid_room_id_with_non_standard_port() {
        assert_eq!(
            RoomId::new("!29fhd83h92h0:example.com:5000")
                .expect("Failed to create RoomId.")
                .to_string(),
            "!29fhd83h92h0:example.com:5000"
        );
    }

    #[test]
    fn missing_room_id_sigil() {
        assert_eq!(
            RoomId::new("carl:example.com").err().unwrap(),
            Error::MissingSigil
        );
    }

    #[test]
    fn missing_room_id_delimiter() {
        assert_eq!(
            RoomId::new("!29fhd83h92h0").err().unwrap(),
            Error::MissingDelimiter
        );
    }

    #[test]
    fn invalid_room_id_host() {
        assert_eq!(
            RoomId::new("!29fhd83h92h0:-").err().unwrap(),
            Error::InvalidHost
        );
    }

    #[test]
    fn invalid_room_id_port() {
        assert_eq!(
            RoomId::new("!29fhd83h92h0:example.com:notaport").err().unwrap(),
            Error::InvalidHost
        );
    }

    #[test]
    fn valid_user_id() {
        assert_eq!(
            UserId::new("@carl:example.com")
                .expect("Failed to create UserId.")
                .to_string(),
            "@carl:example.com"
        );
    }

    #[test]
    fn valid_user_id_with_explicit_standard_port() {
        assert_eq!(
            UserId::new("@carl:example.com:443")
                .expect("Failed to create UserId.")
                .to_string(),
            "@carl:example.com"
        );
    }

    #[test]
    fn valid_user_id_with_non_standard_port() {
        assert_eq!(
            UserId::new("@carl:example.com:5000")
                .expect("Failed to create UserId.")
                .to_string(),
            "@carl:example.com:5000"
        );
    }

    #[test]
    fn invalid_characters_in_user_id_localpart() {
        assert_eq!(
            UserId::new("@CARL:example.com").err().unwrap(),
            Error::InvalidCharacters
        );
    }

    #[test]
    fn missing_user_id_sigil() {
        assert_eq!(
            UserId::new("carl:example.com").err().unwrap(),
            Error::MissingSigil
        );
    }

    #[test]
    fn missing_user_id_delimiter() {
        assert_eq!(
            UserId::new("@carl").err().unwrap(),
            Error::MissingDelimiter
        );
    }

    #[test]
    fn invalid_user_id_host() {
        assert_eq!(
            UserId::new("@carl:-").err().unwrap(),
            Error::InvalidHost
        );
    }

    #[test]
    fn invalid_user_id_port() {
        assert_eq!(
            UserId::new("@carl:example.com:notaport").err().unwrap(),
            Error::InvalidHost
        );
    }
}
<|endoftext|>


<commit_before>//!
//! ```
//! use document::Package;
//! let package = Package::new();
//! let doc = package.as_document();
//!
//! let hello = doc.create_element("hello");
//! hello.set_attribute_value("planet", "Earth");
//! let comment = doc.create_comment("What about other planets?");
//! let text = doc.create_text("Greetings, Earthlings!");
//!
//! hello.append_child(comment);
//! hello.append_child(text);
//! doc.root().append_child(hello);
//! ```
//!
//! ### Memory and ownership
//!
//! The `Package` struct is responsible for owning every node in the
//! document. Strings are interned, allowing repeated text to consume
//! less memory. This is very useful for documents containing lots of
//! the same attributes and tag names.
//!
//! The flip side of this decision is that allocated nodes and strings
//! are not deallocated until the entire `Package` is dropped. This is
//! a reasonable decision for two common cases: building up an XML
//! document and reading an XML document. You may wish to perform
//! large modifications to your data *before* creating a document.
//!
//! ### Namespaces, QNames, and Prefixes
//!
//! The names of elements and attributes may use namespaces. XML
//! namespacing uses URIs to uniquely distinguish items with the same
//! local name. A qualified name (`QName`) combines this optional URI
//! with the local name.
//!
//! When an XML document is represented as text, namespaces are given
//! a shorthand reference known as a prefix. Prefix names are
//! non-authoritative, and only the URI can be used to namespace a
//! name.
//!
//! Elements and attributes may specify a *preferred prefix*, which is
//! an indication of what the user would like to be used as a
//! prefix. There are times where the preferred prefix would cause a
//! conflict, and so an autogenerated prefix will be used instead.
//!
//! ### Design decisions
//!
//! Try to leverage the type system as much as possible.

#![crate_name = "document"]

#![feature(core)]
#![feature(rustc_private)]
#![feature(std_misc)]
#![feature(str_char)]
#![feature(test)]

extern crate arena;
extern crate test;

#[macro_use]
extern crate peresil;

use std::fmt;

mod str_ext;
mod string_pool;
mod raw;
mod str;

#[doc(hidden)]
pub mod thindom4;
pub mod dom4;
pub mod parser;
pub mod writer;

pub use str::XmlChar;

/// A prefixed name. This represents what is found in the string form
/// of an XML document, and does not apply any namespace mapping.
#[derive(Debug,Copy,Clone,PartialEq)]
pub struct PrefixedName<'a> {
    prefix: Option<&'a str>,
    local_part: &'a str,
}

impl<'a> PrefixedName<'a> {
    /// Create a `PrefixedName` without a prefix
    pub fn new(local_part: &str) -> PrefixedName {
        PrefixedName::with_prefix(None, local_part)
    }

    /// Create a `PrefixedName` without an optional prefix
    pub fn with_prefix(prefix: Option<&'a str>, local_part: &'a str) -> PrefixedName<'a> {
        PrefixedName {
            prefix: prefix,
            local_part: local_part,
        }
    }

    pub fn prefix(&self) -> Option<&str> { self.prefix }
    pub fn local_part(&self) -> &str { self.local_part }
}

/// A namespace-qualified name. This represents the name of an element
/// or attribute *after* the prefix has been mapped to a specific
/// namespace.
#[derive(Debug,Copy,Clone,PartialEq)]
pub struct QName<'s> {
    namespace_uri: Option<&'s str>,
    local_part: &'s str,
}

impl<'s> QName<'s> {
    /// Create a `QName` without a namespace
    pub fn new(local_part: &'s str) -> QName<'s> {
        QName::with_namespace_uri(None, local_part)
    }

    /// Create a `QName` with an optional namespace
    pub fn with_namespace_uri(namespace_uri: Option<&'s str>, local_part: &'s str) -> QName<'s> {
        QName {
            namespace_uri: namespace_uri,
            local_part: local_part,
        }
    }

    pub fn namespace_uri(&self) -> Option<&'s str> { self.namespace_uri }
    pub fn local_part(&self) -> &'s str { self.local_part }
}

/// Convert item into a `QName`
pub trait IntoQName<'s> {
    fn into_qname(self) -> QName<'s>;
}

impl<'s> IntoQName<'s> for QName<'s> {
    fn into_qname(self) -> QName<'s> { self }
}

impl<'s> IntoQName<'s> for (&'s str, &'s str) {
    fn into_qname(self) -> QName<'s> { QName { namespace_uri: Some(self.0), local_part: self.1 } }
}

impl<'s> IntoQName<'s> for &'s str {
    fn into_qname(self) -> QName<'s> { QName { namespace_uri: None, local_part: self } }
}

/// The main entrypoint to an XML document
///
/// This is an opaque structure that stores the internal details of
/// the XML document. Modify the document via `as_document`.
pub struct Package {
    storage: raw::Storage,
    connections: raw::Connections,
}

impl Package {
    pub fn new() -> Package {
        let s = raw::Storage::new();
        let root = s.create_root();
        Package {
            storage: s,
            connections: raw::Connections::new(root),
        }
    }

    pub fn as_document(&self) -> dom4::Document {
        dom4::Document::new(&self.storage, &self.connections)
    }

    #[doc(hidden)]
    pub fn as_thin_document(&self) -> (thindom4::Storage, thindom4::Connections) {
        let s = thindom4::Storage::new(&self.storage);
        let c = thindom4::Connections::new(&self.connections);
        (s, c)
    }
}

impl PartialEq for Package {
    fn eq(&self, other: &Package) -> bool {
        self as *const Package == other as *const Package
    }
}

impl fmt::Debug for Package {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Package")
    }
}
<commit_msg>std_misc is stable<commit_after>//!
//! ```
//! use document::Package;
//! let package = Package::new();
//! let doc = package.as_document();
//!
//! let hello = doc.create_element("hello");
//! hello.set_attribute_value("planet", "Earth");
//! let comment = doc.create_comment("What about other planets?");
//! let text = doc.create_text("Greetings, Earthlings!");
//!
//! hello.append_child(comment);
//! hello.append_child(text);
//! doc.root().append_child(hello);
//! ```
//!
//! ### Memory and ownership
//!
//! The `Package` struct is responsible for owning every node in the
//! document. Strings are interned, allowing repeated text to consume
//! less memory. This is very useful for documents containing lots of
//! the same attributes and tag names.
//!
//! The flip side of this decision is that allocated nodes and strings
//! are not deallocated until the entire `Package` is dropped. This is
//! a reasonable decision for two common cases: building up an XML
//! document and reading an XML document. You may wish to perform
//! large modifications to your data *before* creating a document.
//!
//! ### Namespaces, QNames, and Prefixes
//!
//! The names of elements and attributes may use namespaces. XML
//! namespacing uses URIs to uniquely distinguish items with the same
//! local name. A qualified name (`QName`) combines this optional URI
//! with the local name.
//!
//! When an XML document is represented as text, namespaces are given
//! a shorthand reference known as a prefix. Prefix names are
//! non-authoritative, and only the URI can be used to namespace a
//! name.
//!
//! Elements and attributes may specify a *preferred prefix*, which is
//! an indication of what the user would like to be used as a
//! prefix. There are times where the preferred prefix would cause a
//! conflict, and so an autogenerated prefix will be used instead.
//!
//! ### Design decisions
//!
//! Try to leverage the type system as much as possible.

#![crate_name = "document"]

#![feature(core)]
#![feature(rustc_private)]
#![feature(str_char)]
#![feature(test)]

extern crate arena;
extern crate test;

#[macro_use]
extern crate peresil;

use std::fmt;

mod str_ext;
mod string_pool;
mod raw;
mod str;

#[doc(hidden)]
pub mod thindom4;
pub mod dom4;
pub mod parser;
pub mod writer;

pub use str::XmlChar;

/// A prefixed name. This represents what is found in the string form
/// of an XML document, and does not apply any namespace mapping.
#[derive(Debug,Copy,Clone,PartialEq)]
pub struct PrefixedName<'a> {
    prefix: Option<&'a str>,
    local_part: &'a str,
}

impl<'a> PrefixedName<'a> {
    /// Create a `PrefixedName` without a prefix
    pub fn new(local_part: &str) -> PrefixedName {
        PrefixedName::with_prefix(None, local_part)
    }

    /// Create a `PrefixedName` without an optional prefix
    pub fn with_prefix(prefix: Option<&'a str>, local_part: &'a str) -> PrefixedName<'a> {
        PrefixedName {
            prefix: prefix,
            local_part: local_part,
        }
    }

    pub fn prefix(&self) -> Option<&str> { self.prefix }
    pub fn local_part(&self) -> &str { self.local_part }
}

/// A namespace-qualified name. This represents the name of an element
/// or attribute *after* the prefix has been mapped to a specific
/// namespace.
#[derive(Debug,Copy,Clone,PartialEq)]
pub struct QName<'s> {
    namespace_uri: Option<&'s str>,
    local_part: &'s str,
}

impl<'s> QName<'s> {
    /// Create a `QName` without a namespace
    pub fn new(local_part: &'s str) -> QName<'s> {
        QName::with_namespace_uri(None, local_part)
    }

    /// Create a `QName` with an optional namespace
    pub fn with_namespace_uri(namespace_uri: Option<&'s str>, local_part: &'s str) -> QName<'s> {
        QName {
            namespace_uri: namespace_uri,
            local_part: local_part,
        }
    }

    pub fn namespace_uri(&self) -> Option<&'s str> { self.namespace_uri }
    pub fn local_part(&self) -> &'s str { self.local_part }
}

/// Convert item into a `QName`
pub trait IntoQName<'s> {
    fn into_qname(self) -> QName<'s>;
}

impl<'s> IntoQName<'s> for QName<'s> {
    fn into_qname(self) -> QName<'s> { self }
}

impl<'s> IntoQName<'s> for (&'s str, &'s str) {
    fn into_qname(self) -> QName<'s> { QName { namespace_uri: Some(self.0), local_part: self.1 } }
}

impl<'s> IntoQName<'s> for &'s str {
    fn into_qname(self) -> QName<'s> { QName { namespace_uri: None, local_part: self } }
}

/// The main entrypoint to an XML document
///
/// This is an opaque structure that stores the internal details of
/// the XML document. Modify the document via `as_document`.
pub struct Package {
    storage: raw::Storage,
    connections: raw::Connections,
}

impl Package {
    pub fn new() -> Package {
        let s = raw::Storage::new();
        let root = s.create_root();
        Package {
            storage: s,
            connections: raw::Connections::new(root),
        }
    }

    pub fn as_document(&self) -> dom4::Document {
        dom4::Document::new(&self.storage, &self.connections)
    }

    #[doc(hidden)]
    pub fn as_thin_document(&self) -> (thindom4::Storage, thindom4::Connections) {
        let s = thindom4::Storage::new(&self.storage);
        let c = thindom4::Connections::new(&self.connections);
        (s, c)
    }
}

impl PartialEq for Package {
    fn eq(&self, other: &Package) -> bool {
        self as *const Package == other as *const Package
    }
}

impl fmt::Debug for Package {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Package")
    }
}
<|endoftext|>


<commit_before>#![feature(libc)]
extern crate libc;

#[link(name = "groonga")]
extern {
    pub fn grn_init() -> u64;
    pub fn grn_ctx_open(rc: i32) -> u64;
    pub fn grn_ctx_close(ctx: *mut libc::c_void) -> u64;
    pub fn grn_db_open(ctx: *mut libc::c_void, dbpath: *mut libc::c_char) -> *mut libc::c_void;
    pub fn grn_db_create(ctx: *mut libc::c_void, dbpath: *mut libc::c_char,
                         flag: Option<extern "C" fn(libc::c_int) -> libc::c_int>) -> *mut libc::c_void;
    pub fn grn_ctx_send(ctx: *mut libc::c_void, command: libc::c_char, command_length: libc::c_int,
                        flag: libc::c_int) -> u64;
    pub fn grn_ctx_recv(ctx: *mut libc::c_void, command: *mut libc::c_char, command_length: *mut libc::c_int,
                        flag: *mut libc::c_int) -> u64;
    pub fn grn_get_version() -> libc::c_char;
    pub fn grn_fin() -> u64;
}
<commit_msg>Fix return types<commit_after>#![feature(libc)]
extern crate libc;

#[link(name = "groonga")]
extern {
    pub fn grn_init() -> u64;
    pub fn grn_ctx_open(rc: u64) -> *mut libc::c_void;
    pub fn grn_ctx_close(ctx: *mut libc::c_void) -> u64;
    pub fn grn_db_open(ctx: *mut libc::c_void, dbpath: *mut libc::c_char) -> *mut libc::c_void;
    pub fn grn_db_create(ctx: *mut libc::c_void, dbpath: *mut libc::c_char,
                         flag: Option<extern "C" fn(libc::c_int) -> libc::c_int>) -> *mut libc::c_void;
    pub fn grn_ctx_send(ctx: *mut libc::c_void, command: libc::c_char, command_length: libc::c_int,
                        flag: libc::c_int) -> u64;
    pub fn grn_ctx_recv(ctx: *mut libc::c_void, command: *mut libc::c_char, command_length: *mut libc::c_int,
                        flag: *mut libc::c_int) -> u64;
    pub fn grn_get_version() -> *mut libc::c_char;
    pub fn grn_fin() -> u64;
}
<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_char, c_int};

#[repr(C)]
#[derive(Copy)]
pub struct PamHandle;

#[repr(C)]
#[derive(Copy)]
pub struct PamConv;

extern "C" {
    /* --------------             pam_appl.h            ------------- */
    /* -------------- The Linux-PAM Framework layer API ------------- */

    pub fn pam_start(service_name: *const c_char, user: *const c_char,
        pam_conversation: *const PamConv, pamh: *mut *mut PamHandle) -> c_int;

    pub fn pam_end(pamh: *const PamHandle, pam_status: c_int) -> c_int;

    /* Authentication API's */
    pub fn pam_authenticate(pamh: *mut PamHandle, flags: c_int) -> c_int;

    pub fn pam_setcred(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Account Management API's */
    pub fn pam_acct_mgmt(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Session Management API's */
    pub fn pam_open_session(pamh: *mut PamHandle, flags: c_int) -> c_int;

    pub fn pam_close_session(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Password Management API's */
    pub fn pam_chauthtok(pamh: *mut PamHandle, flags: c_int) -> c_int;
}
<commit_msg>Testing a changed signature<commit_after>extern crate libc;

use libc::{c_char, c_int};

#[repr(C)]
#[derive(Copy)]
pub struct PamHandle;

#[repr(C)]
#[derive(Copy)]
pub struct PamConv;

extern "C" {
    /* --------------             pam_appl.h            ------------- */
    /* -------------- The Linux-PAM Framework layer API ------------- */

    pub fn pam_start(service_name: *const c_char, user: *const c_char,
        pam_conversation: *const PamConv, pamh: *mut *const PamHandle) -> c_int;

    pub fn pam_end(pamh: *const PamHandle, pam_status: c_int) -> c_int;

    /* Authentication API's */
    pub fn pam_authenticate(pamh: *mut PamHandle, flags: c_int) -> c_int;

    pub fn pam_setcred(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Account Management API's */
    pub fn pam_acct_mgmt(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Session Management API's */
    pub fn pam_open_session(pamh: *mut PamHandle, flags: c_int) -> c_int;

    pub fn pam_close_session(pamh: *mut PamHandle, flags: c_int) -> c_int;

    /* Password Management API's */
    pub fn pam_chauthtok(pamh: *mut PamHandle, flags: c_int) -> c_int;
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 - Jeremiah Peschka
//
// This file is provided to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file
// except in compliance with the License.  You may obtain
// a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
pub mod flaker {

    extern crate time;
    extern crate num;
    extern crate byteorder;
    use self::num::BigUint;
    use self::byteorder::{LittleEndian, WriteBytesExt};

    #[derive(Debug)]
    pub enum FlakeError {
        ClockIsRunningBackwards
    }

    pub trait HasFlakes {
        fn update(&mut self) -> Result<(), FlakeError>;
        fn get_id(&mut self) -> Result<BigUint, FlakeError>;
    }

    pub struct Flaker {
        identifier: Vec<u8>,
        last_generated_time_ms: u64,
        counter: u16,
    }

    impl Flaker {
        /// Returns a new Flaker based on the specified identifier
        ///
        /// # Arguments
        ///
        /// * `identifier` - A 6 byte vec that provides some arbitrary identification.
        pub fn new_from_identifier(identifier: Vec<u8>) -> Flaker {
            Flaker::new(identifier, false)
        }

        /// Returns a new Flaker based on the specified identifier
        ///
        /// # Arguments
        ///
        /// * `identifier` - A 6 byte vec that provides some arbitrary identification.
        /// * `little_endian` - You know, just in case you have a little endian system. This is important for byte order when constructing the flake.
        pub fn new(identifier: Vec<u8>, little_endian: bool) -> Flaker {
            let mut l_identifier = identifier.clone();

            if l_identifier.len() < 6 {
                panic!("Identifier must have a length of 6");
            }

            if !little_endian {
                l_identifier.reverse();
            }

            Flaker { identifier: l_identifier,
                    last_generated_time_ms: Flaker::current_time_in_ms(),
                    counter: 0
                }
        }

        /// Returns the current UNIX time in milliseconds
        fn current_time_in_ms() -> u64 {
            let now = time::now();
            let now_ts = now.to_timespec();

            now_ts.sec as u64 + (now_ts.nsec as u64 / 1000 / 1000)
        }

        /// Creates a new flake ID from the identifier, current time, and an internal counter.
        /// Identifiers are generated as 128-bit numbers:
        /// * 64-bit timestamp as milliseconds since the dawn of time (January 1, 1970)
        /// * 48-bit worker identifier
        /// * 16-bit sequence number that is incremented when more than one identifier is requested in the same millisecond and reset to 0 when the clock moves forward
        fn construct_id(&mut self) -> BigUint {
            // Create a new vec of bytes
            // let mut bytes = Vec::new();
            let mut bytes = [0 as u8; 8];

            // push the counter into bytes
            bytes[0] = self.counter as u8;
            // bytes.push(self.counter as u8);
            bytes[1] = (self.counter >> 8) as u8;
            // bytes.push((self.counter >> 8) as u8);

            // next 6 bytes are the worker id
            for (pos, byte) in &self.identifier.iter().enumerate() {
                bytes[pos + 2] = byte;
            }

            let mut wtr = vec![];

            wtr.write_u64::<LittleEndian>(self.last_generated_time_ms).unwrap();

            for (pos, w) in wtr.into_iter().enumerate() {
                bytes[pos + 4] = w;
                // bytes.push(w);
            }

            BigUint::from_bytes_le(&bytes)
        }
    }

    impl HasFlakes for Flaker {
        /// Update internal data structures.
        fn update(&mut self) -> Result<(), FlakeError> {
            let current_time_in_ms = Flaker::current_time_in_ms();

            if self.last_generated_time_ms > current_time_in_ms {
                return Result::Err(FlakeError::ClockIsRunningBackwards);
            }

            if self.last_generated_time_ms < current_time_in_ms {
                self.counter = 0;
            }
            else {
                self.counter += 1;
            }

            self.last_generated_time_ms = current_time_in_ms;

            Ok(())
        }

        /// Generate a new ID
        fn get_id(&mut self) -> Result<BigUint, FlakeError> {
            let update_result = self.update();

            match update_result {
                Ok(_) => Ok(self.construct_id()),
                Err(err) => Err(err),
            }
        }
    }

    #[test]
    fn ids_change_over_time() {
        use std::time::Duration;
        use std::thread;

        let mut f1 = Flaker::new_from_identifier(vec![0, 1, 2, 3, 4, 5]);
        let id1 = f1.get_id().unwrap();
        thread::sleep(Duration::from_millis(50));
        let id2 = f1.get_id().unwrap();

        println!("{} < {}", id1, id2);

        assert!(id1 < id2);
    }

    #[test]
    fn ids_change_quickly() {
        let mut f1 = Flaker::new_from_identifier(vec![0, 1, 2, 3, 4, 5]);

        let id3 = f1.get_id().unwrap();
        let id4 = f1.get_id().unwrap();

        assert!(id3 < id4);
    }
}<commit_msg>Moving to using an array in construct_id<commit_after>// Copyright (c) 2016 - Jeremiah Peschka
//
// This file is provided to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file
// except in compliance with the License.  You may obtain
// a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
pub mod flaker {

    extern crate time;
    extern crate num;
    extern crate byteorder;
    use self::num::BigUint;
    use self::byteorder::{LittleEndian, WriteBytesExt};

    #[derive(Debug)]
    pub enum FlakeError {
        ClockIsRunningBackwards
    }

    pub trait HasFlakes {
        fn update(&mut self) -> Result<(), FlakeError>;
        fn get_id(&mut self) -> Result<BigUint, FlakeError>;
    }

    pub struct Flaker {
        identifier: Vec<u8>,
        last_generated_time_ms: u64,
        counter: u16,
    }

    impl Flaker {
        /// Returns a new Flaker based on the specified identifier
        ///
        /// # Arguments
        ///
        /// * `identifier` - A 6 byte vec that provides some arbitrary identification.
        pub fn new_from_identifier(identifier: Vec<u8>) -> Flaker {
            Flaker::new(identifier, false)
        }

        /// Returns a new Flaker based on the specified identifier
        ///
        /// # Arguments
        ///
        /// * `identifier` - A 6 byte vec that provides some arbitrary identification.
        /// * `little_endian` - You know, just in case you have a little endian system. This is important for byte order when constructing the flake.
        pub fn new(identifier: Vec<u8>, little_endian: bool) -> Flaker {
            let mut l_identifier = identifier.clone();

            if l_identifier.len() < 6 {
                panic!("Identifier must have a length of 6");
            }

            if !little_endian {
                l_identifier.reverse();
            }

            Flaker { identifier: l_identifier,
                    last_generated_time_ms: Flaker::current_time_in_ms(),
                    counter: 0
                }
        }

        /// Returns the current UNIX time in milliseconds
        fn current_time_in_ms() -> u64 {
            let now = time::now();
            let now_ts = now.to_timespec();

            now_ts.sec as u64 + (now_ts.nsec as u64 / 1000 / 1000)
        }

        /// Creates a new flake ID from the identifier, current time, and an internal counter.
        /// Identifiers are generated as 128-bit numbers:
        /// * 64-bit timestamp as milliseconds since the dawn of time (January 1, 1970)
        /// * 48-bit worker identifier
        /// * 16-bit sequence number that is incremented when more than one identifier is requested in the same millisecond and reset to 0 when the clock moves forward
        fn construct_id(&mut self) -> BigUint {
            // Create a new vec of bytes
            // let mut bytes = Vec::new();
            let mut bytes = [0 as u8; 16];

            // push the counter into bytes
            bytes[0] = self.counter as u8;
            bytes[1] = (self.counter >> 8) as u8;

            // next 6 bytes are the worker id
            for (pos, byte) in self.identifier.iter().enumerate() {
                bytes[pos + 2] = *byte;
            }

            let mut wtr = vec![];

            wtr.write_u64::<LittleEndian>(self.last_generated_time_ms).unwrap();

            for (pos, w) in wtr.into_iter().enumerate() {
                bytes[pos + 8] = w;
            }

            BigUint::from_bytes_le(&bytes)
        }
    }

    impl HasFlakes for Flaker {
        /// Update internal data structures.
        fn update(&mut self) -> Result<(), FlakeError> {
            let current_time_in_ms = Flaker::current_time_in_ms();

            if self.last_generated_time_ms > current_time_in_ms {
                return Result::Err(FlakeError::ClockIsRunningBackwards);
            }

            if self.last_generated_time_ms < current_time_in_ms {
                self.counter = 0;
            }
            else {
                self.counter += 1;
            }

            self.last_generated_time_ms = current_time_in_ms;

            Ok(())
        }

        /// Generate a new ID
        fn get_id(&mut self) -> Result<BigUint, FlakeError> {
            let update_result = self.update();

            match update_result {
                Ok(_) => Ok(self.construct_id()),
                Err(err) => Err(err),
            }
        }
    }

    #[test]
    fn ids_change_over_time() {
        use std::time::Duration;
        use std::thread;
                let mut f1 = Flaker::new_from_identifier(vec![0, 1, 2, 3, 4, 5]);
        let id1 = f1.get_id().unwrap();
        thread::sleep(Duration::from_millis(50));
        let id2 = f1.get_id().unwrap();

        println!("{} < {}", id1, id2);

        assert!(id1 < id2);
    }

    #[test]
    fn ids_change_quickly() {
        let mut f1 = Flaker::new_from_identifier(vec![0, 1, 2, 3, 4, 5]);

        let id3 = f1.get_id().unwrap();
        let id4 = f1.get_id().unwrap();

        println!("{} < {}", id3, id4);

        assert!(id3 < id4);
    }
}<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! SOS: the Stupid Operating System
//!
//! SOS is a simple, tiny toy OS implemented in Rust.
//!
//! I'm writing this mostly for fun, to learn more about OS design and kernel //! hacking, so don't expect anything new or exciting out of this project.

#![crate_name = "sos_kernel"]
#![crate_type = "staticlib"]
#![feature( no_std
          , lang_items
          , asm )]
#![feature( const_fn
          , core_slice_ext
          , slice_patterns
          , iter_cmp
          )]
#![feature(collections)]

#![no_std]

extern crate collections;
extern crate rlibc;
extern crate spin;

extern crate sos_multiboot2 as multiboot;
extern crate sos_alloc as alloc;

#[macro_use] extern crate sos_vga as vga;
#[macro_use] extern crate bitflags;

pub mod arch;
#[macro_use] pub mod io;
pub mod util;
pub mod panic;
pub mod memory;

use arch::cpu;

/// Kernel main loop
pub fn kernel_main() {
    loop { }
}

/// Kernel initialization function called from ASM
///
/// The kernel main loop expects to be passed the address of a valid
/// Multiboot 2 info struct. It's the bootloader's responsibility to ensure
/// that this is passed in the correct register as expected by the calling
/// convention (`edi` on x86). If this isn't there, you can expect to have a
/// bad problem and not go to space today.
#[no_mangle]
pub extern fn kernel_start(multiboot_addr: usize) {
    io::term::CONSOLE.lock().clear();

    println!("Hello from the kernel!");

    // -- Unpack multiboot tag ------------------------------------------------
    let boot_info = unsafe { multiboot::Info::from(multiboot_addr) };

    let mmap_tag // Extract the memory map tag from the multiboot info
        = boot_info.mem_map()
                   .expect("Memory map tag required!");

    println!("Detected memory areas:");
    for a in mmap_tag.areas() {
        println!("     start: {:#08x}, end: {:#08x}"
                , a.base, a.length );
    }

    let elf_sections_tag // Extract ELF sections tag from the multiboot info
        = boot_info.elf64_sections()
                   .expect("ELF sections tag required!");

    println!("Detecting kernel ELF sections:");

    let kernel_begin    // Extract kernel ELF sections from  multiboot info
        = elf_sections_tag.sections()
            .map(|s| {
                println!("     address: {:#08x}, size: {:#08x}, flags: {:#08x}"
                        , s.address
                        , s.length
                        , s.flags );
                s.address })
            .min()
            .expect("Could not find kernel start section!\
                    \nSomething is deeply wrong.");

    let mut n_elf_sections = 0;
    let kernel_end
        = elf_sections_tag.sections()
            .map(|s| { n_elf_sections += 1;
                     s.address })
            .max()
            .expect("Could not find kernel end section!\
                    \nSomething is deeply wrong.");

    println!( "Detected {} kernel ELF sections.", n_elf_sections);
    println!( "Kernel begins at {:#x} and ends at {:#x}."
             , kernel_begin, kernel_end );

    let multiboot_end = multiboot_addr + boot_info.length as usize;

    println!( "Multiboot info begins at {:#x} and ends at {:#x}."
             , multiboot_addr, multiboot_end);

    // -- initialize interrupts ----------------------------------------------
    unsafe {
        cpu::interrupts::initialize();
        println!("Intializing interrupts...    [DONE]" );
    };

    // -- initialize the heap ------------------------------------------------
    unsafe {
        print!("Intializing heap...    ");
        memory::init_heap();
        println!( "[DONE]\nHeap begins at {:#x} and ends at {:#x}."
                , memory::heap_base_addr(), memory::heap_top_addr() );
    };

    // let mut a_vec = collections::vec::Vec::<usize>::new();
    // println!( "TEST: Created a vector in kernel space! {:?}", a_vec);
    // a_vec.push(1);
    // println!( "TEST: pushed to vec: {:?}", a_vec);
    // a_vec.push(2);
    // println!( "TEST: pushed to vec: {:?}", a_vec);

    // -- call into kernel main loop ------------------------------------------
    // (currently, this does nothing)
    kernel_main()
}
<commit_msg>Remove unused features<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! SOS: the Stupid Operating System
//!
//! SOS is a simple, tiny toy OS implemented in Rust.
//!
//! I'm writing this mostly for fun, to learn more about OS design and kernel //! hacking, so don't expect anything new or exciting out of this project.

#![crate_name = "sos_kernel"]
#![crate_type = "staticlib"]
#![feature( no_std
          , lang_items
          , asm )]
#![feature( const_fn
          , slice_patterns
          )]
#![feature(collections)]
#![no_std]

extern crate collections;
extern crate rlibc;
extern crate spin;

extern crate sos_multiboot2 as multiboot;
extern crate sos_alloc as alloc;

#[macro_use] extern crate sos_vga as vga;
#[macro_use] extern crate bitflags;

pub mod arch;
pub mod util;
pub mod panic;
pub mod memory;

#[macro_use] pub mod io;

use arch::cpu;

/// Kernel main loop
pub fn kernel_main() {
    loop { }
}

/// Kernel initialization function called from ASM
///
/// The kernel main loop expects to be passed the address of a valid
/// Multiboot 2 info struct. It's the bootloader's responsibility to ensure
/// that this is passed in the correct register as expected by the calling
/// convention (`edi` on x86). If this isn't there, you can expect to have a
/// bad problem and not go to space today.
#[no_mangle]
pub extern fn kernel_start(multiboot_addr: usize) {
    io::term::CONSOLE.lock().clear();

    println!("Hello from the kernel!");

    // -- Unpack multiboot tag ------------------------------------------------
    let boot_info = unsafe { multiboot::Info::from(multiboot_addr) };

    let mmap_tag // Extract the memory map tag from the multiboot info
        = boot_info.mem_map()
                   .expect("Memory map tag required!");

    println!("Detected memory areas:");
    for a in mmap_tag.areas() {
        println!("     start: {:#08x}, end: {:#08x}"
                , a.base, a.length );
    }

    let elf_sections_tag // Extract ELF sections tag from the multiboot info
        = boot_info.elf64_sections()
                   .expect("ELF sections tag required!");

    println!("Detecting kernel ELF sections:");

    let kernel_begin    // Extract kernel ELF sections from  multiboot info
        = elf_sections_tag.sections()
            .map(|s| {
                println!("     address: {:#08x}, size: {:#08x}, flags: {:#08x}"
                        , s.address
                        , s.length
                        , s.flags );
                s.address })
            .min()
            .expect("Could not find kernel start section!\
                    \nSomething is deeply wrong.");

    let mut n_elf_sections = 0;
    let kernel_end
        = elf_sections_tag.sections()
            .map(|s| { n_elf_sections += 1;
                     s.address })
            .max()
            .expect("Could not find kernel end section!\
                    \nSomething is deeply wrong.");

    println!( "Detected {} kernel ELF sections.", n_elf_sections);
    println!( "Kernel begins at {:#x} and ends at {:#x}."
             , kernel_begin, kernel_end );

    let multiboot_end = multiboot_addr + boot_info.length as usize;

    println!( "Multiboot info begins at {:#x} and ends at {:#x}."
             , multiboot_addr, multiboot_end);

    // -- initialize interrupts ----------------------------------------------
    unsafe {
        cpu::interrupts::initialize();
        println!("Intializing interrupts...    [DONE]" );
    };

    // -- initialize the heap ------------------------------------------------
    unsafe {
        print!("Intializing heap...    ");
        memory::init_heap();
        println!( "[DONE]\nHeap begins at {:#x} and ends at {:#x}."
                , memory::heap_base_addr(), memory::heap_top_addr() );
    };

    // let mut a_vec = collections::vec::Vec::<usize>::new();
    // println!( "TEST: Created a vector in kernel space! {:?}", a_vec);
    // a_vec.push(1);
    // println!( "TEST: pushed to vec: {:?}", a_vec);
    // a_vec.push(2);
    // println!( "TEST: pushed to vec: {:?}", a_vec);

    // -- call into kernel main loop ------------------------------------------
    // (currently, this does nothing)
    kernel_main()
}
<|endoftext|>