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

//! Module containing `Task` type as well as trait implementations

use uuid::Uuid;

use priority::TaskPriority;
use status::TaskStatus;
use project::Project;
use tag::Tag;
use date::Date;
use annotation::Annotation;

/// Task type
///
/// A task must have four things:
///
/// - A Status
/// - An UUID
/// - An Entry-Date
/// - A Description
///
/// all other Data is optional by taskwarrior. This type is a simple rust representation of the
/// JSON exported by taskwarrior.
///
/// It is deserializeable and serializeable via serde_json, so importing and exporting taskwarrior
/// tasks is simply serializing and deserializing objects of this type.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Task {
    status      : TaskStatus,
    uuid        : Uuid,
    entry       : Date,
    description : String,

    annotations : Vec<Annotation>,
    depends     : Option<String>,
    due         : Option<Date>,
    end         : Option<Date>,
    imask       : Option<i64>,
    mask        : Option<String>,
    modified    : Option<Date>,
    parent      : Option<Uuid>,
    priority    : Option<TaskPriority>,
    project     : Option<Project>,
    recur       : Option<String>,
    scheduled   : Option<Date>,
    start       : Option<Date>,
    tags        : Vec<Tag>,
    until       : Option<Date>,
    wait        : Option<Date>,
}

/*
 * 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 {

    /// Create a new Task instance
    pub fn new(
        status      : TaskStatus,
        uuid        : Uuid,
        entry       : Date,
        description : String,

        annotations : Option<Vec<Annotation>>,
        depends     : Option<String>,
        due         : Option<Date>,
        end         : Option<Date>,
        imask       : Option<i64>,
        mask        : Option<String>,
        modified    : Option<Date>,
        parent      : Option<Uuid>,
        priority    : Option<TaskPriority>,
        project     : Option<Project>,
        recur       : Option<String>,
        scheduled   : Option<Date>,
        start       : Option<Date>,
        tags        : Option<Vec<Tag>>,
        until       : Option<Date>,
        wait        : Option<Date>,
    ) -> Task
    {
        Task {
            status      : status,
            uuid        : uuid,
            entry       : entry,
            description : description,

            annotations : annotations.unwrap_or(vec![]),
            depends     : depends,
            due         : due,
            end         : end,
            imask       : imask,
            mask        : mask,
            modified    : modified,
            parent      : parent,
            priority    : priority,
            project     : project,
            recur       : recur,
            scheduled   : scheduled,
            start       : start,
            tags        : tags.unwrap_or(vec![]),
            until       : until,
            wait        : wait,
        }
    }

    /// Get the status of the task
    pub fn status(&self) -> &TaskStatus {
        &self.status
    }

    /// Get the uuid of the task
    pub fn uuid(&self) -> &Uuid {
        &self.uuid
    }

    /// Get the entry date of the task
    pub fn entry(&self) -> &Date {
        &self.entry
    }

    /// Get the description of the task
    pub fn description(&self) -> &String {
        &self.description
    }

    /// Get the annotations of the task
    pub fn annotations(&self) -> Option<&Vec<Annotation>> {
        Some(&self.annotations)
    }

    /// Add an annotation to this task
    pub fn add_annotation(&mut self, an: Annotation) {
        self.annotations.push(an)
    }

    /// Add annotations to this task
    pub fn add_annotations<I: Iterator<Item = Annotation>>(&mut self, i: I) {
        for item in i {
            self.add_annotation(item)
        }
    }

    /// Get the depends of the task
    ///
    /// This is exported as String by now, which might change in future
    pub fn depends(&self) -> Option<&String> {
        self.depends.as_ref()
    }

    /// Get the due date of the task
    pub fn due(&self) -> Option<&Date> {
        self.due.as_ref()
    }

    /// Get the end date of the task
    pub fn end(&self) -> Option<&Date> {
        self.end.as_ref()
    }

    /// Get the imask of the task
    pub fn imask(&self) -> Option<&i64> {
        self.imask.as_ref()
    }

    /// Get the mask of the task
    pub fn mask(&self) -> Option<&String> {
        self.mask.as_ref()
    }

    /// Get the modified date of the task
    pub fn modified(&self) -> Option<&Date> {
        self.modified.as_ref()
    }

    /// Get the parent of the task
    pub fn parent(&self) -> Option<&Uuid> {
        self.parent.as_ref()
    }

    /// Get the priority of the task
    pub fn priority(&self) -> Option<&TaskPriority> {
        self.priority.as_ref()
    }

    /// Get the project of the task
    pub fn project(&self) -> Option<&Project> {
        self.project.as_ref()
    }

    /// Get the recur of the task
    ///
    /// This is exported as String by now. This might change in future versions of this crate.
    pub fn recur(&self) -> Option<&String> {
        self.recur.as_ref()
    }

    /// Get the scheduled date of the task
    pub fn scheduled(&self) -> Option<&Date> {
        self.scheduled.as_ref()
    }

    /// Get the start date of the task
    pub fn start(&self) -> Option<&Date> {
        self.start.as_ref()
    }

    /// Get the tags of the task
    pub fn tags(&self) -> Option<&Vec<Tag>> {
        Some(&self.tags)
    }

    /// Get the until date of the task
    pub fn until(&self) -> Option<&Date> {
        self.until.as_ref()
    }

    /// Get the wait date of the task
    pub fn wait(&self) -> Option<&Date> {
        self.wait.as_ref()
    }

}

#[cfg(test)]
mod test {
    use date::Date;
    use date::TASKWARRIOR_DATETIME_TEMPLATE;
    use status::TaskStatus;
    use task::Task;
    use annotation::Annotation;

    use uuid::Uuid;
    use chrono::naive::datetime::NaiveDateTime;
    use serde_json;

    fn mkdate(s: &str) -> Date {
        let n = NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE);
        Date::from(n.unwrap())
    }

    #[test]
    fn test_deser() {
        let s =
r#"{
"id": 1,
"description": "test",
"entry": "20150619T165438Z",
"status": "waiting",
"uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"
}"#;

        println!("{}", s);

        let task = serde_json::from_str(s);
        println!("{:?}", task);
        assert!(task.is_ok());
        let task : Task = task.unwrap();

        assert!(task.status().clone() == TaskStatus::Waiting);
        assert!(task.description() == "test");
        assert!(task.entry().clone() == mkdate("20150619T165438Z"));
        assert!(task.uuid().clone() == Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap());

        let back = serde_json::to_string(&task).unwrap();

        assert!(back.contains("description"));
        assert!(back.contains("test"));
        assert!(back.contains("entry"));
        assert!(back.contains("20150619T165438Z"));
        assert!(back.contains("status"));
        assert!(back.contains("waiting"));
        assert!(back.contains("uuid"));
        assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"));
    }

    #[test]
    fn test_deser_more() {
        let s =
r#"{
"id": 1,
"description": "some description",
"entry": "20150619T165438Z",
"modified": "20160327T164007Z",
"project": "someproject",
"status": "waiting",
"tags": ["some", "tags", "are", "here"],
"uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
"wait": "20160508T164007Z",
"urgency": 0.583562
}"#;

        println!("{}", s);

        let task = serde_json::from_str(s);
        println!("{:?}", task);
        assert!(task.is_ok());
        let task : Task = task.unwrap();

        assert!(task.status().clone() == TaskStatus::Waiting);
        assert!(task.description() == "some description");
        assert!(task.entry().clone() == mkdate("20150619T165438Z"));
        assert!(task.uuid().clone() == Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap());

        assert!(task.modified() == Some(&mkdate("20160327T164007Z")));
        assert!(task.project() == Some(&String::from("someproject")));

        if let Some(tags) = task.tags() {
            for tag in tags {
                let any_tag = [ "some", "tags", "are", "here", ]
                    .into_iter().any(|t| tag == *t);
                assert!(any_tag, "Tag {} missing", tag);
            }
        } else {
                assert!(false, "Tags completely missing");
        }

        assert!(task.wait() == Some(&mkdate("20160508T164007Z")));
        // assert!(task.urgency().clone() == 0.583562);

        let back = serde_json::to_string(&task).unwrap();

        assert!(back.contains("description"));
        assert!(back.contains("some description"));
        assert!(back.contains("entry"));
        assert!(back.contains("20150619T165438Z"));
        assert!(back.contains("project"));
        assert!(back.contains("someproject"));
        assert!(back.contains("status"));
        assert!(back.contains("waiting"));
        assert!(back.contains("tags"));
        assert!(back.contains("some"));
        assert!(back.contains("tags"));
        assert!(back.contains("are"));
        assert!(back.contains("here"));
        assert!(back.contains("uuid"));
        assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"));
    }

    #[test]
    fn test_deser_annotation() {
        let s =
r#"{
"id":192,
"description":"Some long description for a task",
"entry":"20160423T125820Z",
"modified":"20160423T125942Z",
"project":"project",
"status":"pending",
"tags":["search","things"],
"uuid":"5a04bb1e-3f4b-49fb-b9ba-44407ca223b5",
"annotations":[{"entry":"20160423T125911Z","description":"An Annotation"},
               {"entry":"20160423T125926Z","description":"Another Annotation"},
               {"entry":"20160422T125942Z","description":"A Third Anno"}
               ],
"urgency":10.911
}"#;

        println!("{}", s);

        let task = serde_json::from_str(s);
        println!("{:?}", task);
        assert!(task.is_ok());
        let task : Task = task.unwrap();

        let all_annotations = vec![
            Annotation::new(mkdate("20160423T125911Z"), String::from("An Annotation")),
            Annotation::new(mkdate("20160423T125926Z"), String::from("Another Annotation")),
            Annotation::new(mkdate("20160422T125942Z"), String::from("A Third Anno"))
        ];

        if let Some(annotations) = task.annotations() {
            for annotation in annotations {
                let r = all_annotations.iter().any(|ann| {
                    let descr = ann.description() == annotation.description();
                    let entry = ann.entry() == annotation.entry();

                    descr && entry
                });
                assert!(r, "Annotation {:?} missing or buggy", annotation);
            }
        } else {
            assert!(false, "Annotations missing");
        }
    }

}

<commit_msg>Because of custom derive, we have to make these optional<commit_after>//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//

//! Module containing `Task` type as well as trait implementations

use uuid::Uuid;

use priority::TaskPriority;
use status::TaskStatus;
use project::Project;
use tag::Tag;
use date::Date;
use annotation::Annotation;

/// Task type
///
/// A task must have four things:
///
/// - A Status
/// - An UUID
/// - An Entry-Date
/// - A Description
///
/// all other Data is optional by taskwarrior. This type is a simple rust representation of the
/// JSON exported by taskwarrior.
///
/// It is deserializeable and serializeable via serde_json, so importing and exporting taskwarrior
/// tasks is simply serializing and deserializing objects of this type.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Task {
    status      : TaskStatus,
    uuid        : Uuid,
    entry       : Date,
    description : String,

    annotations : Option<Vec<Annotation>>,
    depends     : Option<String>,
    due         : Option<Date>,
    end         : Option<Date>,
    imask       : Option<i64>,
    mask        : Option<String>,
    modified    : Option<Date>,
    parent      : Option<Uuid>,
    priority    : Option<TaskPriority>,
    project     : Option<Project>,
    recur       : Option<String>,
    scheduled   : Option<Date>,
    start       : Option<Date>,
    tags        : Option<Vec<Tag>>,
    until       : Option<Date>,
    wait        : Option<Date>,
}

/*
 * 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 {

    /// Create a new Task instance
    pub fn new(
        status      : TaskStatus,
        uuid        : Uuid,
        entry       : Date,
        description : String,

        annotations : Option<Vec<Annotation>>,
        depends     : Option<String>,
        due         : Option<Date>,
        end         : Option<Date>,
        imask       : Option<i64>,
        mask        : Option<String>,
        modified    : Option<Date>,
        parent      : Option<Uuid>,
        priority    : Option<TaskPriority>,
        project     : Option<Project>,
        recur       : Option<String>,
        scheduled   : Option<Date>,
        start       : Option<Date>,
        tags        : Option<Vec<Tag>>,
        until       : Option<Date>,
        wait        : Option<Date>,
    ) -> Task
    {
        Task {
            status      : status,
            uuid        : uuid,
            entry       : entry,
            description : description,

            annotations : annotations,
            depends     : depends,
            due         : due,
            end         : end,
            imask       : imask,
            mask        : mask,
            modified    : modified,
            parent      : parent,
            priority    : priority,
            project     : project,
            recur       : recur,
            scheduled   : scheduled,
            start       : start,
            tags        : tags,
            until       : until,
            wait        : wait,
        }
    }

    /// Get the status of the task
    pub fn status(&self) -> &TaskStatus {
        &self.status
    }

    /// Get the uuid of the task
    pub fn uuid(&self) -> &Uuid {
        &self.uuid
    }

    /// Get the entry date of the task
    pub fn entry(&self) -> &Date {
        &self.entry
    }

    /// Get the description of the task
    pub fn description(&self) -> &String {
        &self.description
    }

    /// Get the annotations of the task
    pub fn annotations(&self) -> Option<&Vec<Annotation>> {
        self.annotations.as_ref()
    }

    /// Add an annotation to this task
    pub fn add_annotation(&mut self, an: Annotation) {
        match self.annotations {
            None => self.annotations = Some(vec![an]),
            Some(ref mut n) => n.push(an),
        }
    }

    /// Add annotations to this task
    pub fn add_annotations<I: Iterator<Item = Annotation>>(&mut self, i: I) {
        for item in i {
            self.add_annotation(item)
        }
    }

    /// Get the depends of the task
    ///
    /// This is exported as String by now, which might change in future
    pub fn depends(&self) -> Option<&String> {
        self.depends.as_ref()
    }

    /// Get the due date of the task
    pub fn due(&self) -> Option<&Date> {
        self.due.as_ref()
    }

    /// Get the end date of the task
    pub fn end(&self) -> Option<&Date> {
        self.end.as_ref()
    }

    /// Get the imask of the task
    pub fn imask(&self) -> Option<&i64> {
        self.imask.as_ref()
    }

    /// Get the mask of the task
    pub fn mask(&self) -> Option<&String> {
        self.mask.as_ref()
    }

    /// Get the modified date of the task
    pub fn modified(&self) -> Option<&Date> {
        self.modified.as_ref()
    }

    /// Get the parent of the task
    pub fn parent(&self) -> Option<&Uuid> {
        self.parent.as_ref()
    }

    /// Get the priority of the task
    pub fn priority(&self) -> Option<&TaskPriority> {
        self.priority.as_ref()
    }

    /// Get the project of the task
    pub fn project(&self) -> Option<&Project> {
        self.project.as_ref()
    }

    /// Get the recur of the task
    ///
    /// This is exported as String by now. This might change in future versions of this crate.
    pub fn recur(&self) -> Option<&String> {
        self.recur.as_ref()
    }

    /// Get the scheduled date of the task
    pub fn scheduled(&self) -> Option<&Date> {
        self.scheduled.as_ref()
    }

    /// Get the start date of the task
    pub fn start(&self) -> Option<&Date> {
        self.start.as_ref()
    }

    /// Get the tags of the task
    pub fn tags(&self) -> Option<&Vec<Tag>> {
        self.tags.as_ref()
    }

    /// Get the until date of the task
    pub fn until(&self) -> Option<&Date> {
        self.until.as_ref()
    }

    /// Get the wait date of the task
    pub fn wait(&self) -> Option<&Date> {
        self.wait.as_ref()
    }

}

#[cfg(test)]
mod test {
    use date::Date;
    use date::TASKWARRIOR_DATETIME_TEMPLATE;
    use status::TaskStatus;
    use task::Task;
    use annotation::Annotation;

    use uuid::Uuid;
    use chrono::naive::datetime::NaiveDateTime;
    use serde_json;

    fn mkdate(s: &str) -> Date {
        let n = NaiveDateTime::parse_from_str(s, TASKWARRIOR_DATETIME_TEMPLATE);
        Date::from(n.unwrap())
    }

    #[test]
    fn test_deser() {
        let s =
r#"{
"id": 1,
"description": "test",
"entry": "20150619T165438Z",
"status": "waiting",
"uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"
}"#;

        println!("{}", s);

        let task = serde_json::from_str(s);
        println!("{:?}", task);
        assert!(task.is_ok());
        let task : Task = task.unwrap();

        assert!(task.status().clone() == TaskStatus::Waiting);
        assert!(task.description() == "test");
        assert!(task.entry().clone() == mkdate("20150619T165438Z"));
        assert!(task.uuid().clone() == Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap());

        let back = serde_json::to_string(&task).unwrap();

        assert!(back.contains("description"));
        assert!(back.contains("test"));
        assert!(back.contains("entry"));
        assert!(back.contains("20150619T165438Z"));
        assert!(back.contains("status"));
        assert!(back.contains("waiting"));
        assert!(back.contains("uuid"));
        assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"));
    }

    #[test]
    fn test_deser_more() {
        let s =
r#"{
"id": 1,
"description": "some description",
"entry": "20150619T165438Z",
"modified": "20160327T164007Z",
"project": "someproject",
"status": "waiting",
"tags": ["some", "tags", "are", "here"],
"uuid": "8ca953d5-18b4-4eb9-bd56-18f2e5b752f0",
"wait": "20160508T164007Z",
"urgency": 0.583562
}"#;

        println!("{}", s);

        let task = serde_json::from_str(s);
        println!("{:?}", task);
        assert!(task.is_ok());
        let task : Task = task.unwrap();

        assert!(task.status().clone() == TaskStatus::Waiting);
        assert!(task.description() == "some description");
        assert!(task.entry().clone() == mkdate("20150619T165438Z"));
        assert!(task.uuid().clone() == Uuid::parse_str("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0").unwrap());

        assert!(task.modified() == Some(&mkdate("20160327T164007Z")));
        assert!(task.project() == Some(&String::from("someproject")));

        if let Some(tags) = task.tags() {
            for tag in tags {
                let any_tag = [ "some", "tags", "are", "here", ]
                    .into_iter().any(|t| tag == *t);
                assert!(any_tag, "Tag {} missing", tag);
            }
        } else {
                assert!(false, "Tags completely missing");
        }

        assert!(task.wait() == Some(&mkdate("20160508T164007Z")));
        // assert!(task.urgency().clone() == 0.583562);

        let back = serde_json::to_string(&task).unwrap();

        assert!(back.contains("description"));
        assert!(back.contains("some description"));
        assert!(back.contains("entry"));
        assert!(back.contains("20150619T165438Z"));
        assert!(back.contains("project"));
        assert!(back.contains("someproject"));
        assert!(back.contains("status"));
        assert!(back.contains("waiting"));
        assert!(back.contains("tags"));
        assert!(back.contains("some"));
        assert!(back.contains("tags"));
        assert!(back.contains("are"));
        assert!(back.contains("here"));
        assert!(back.contains("uuid"));
        assert!(back.contains("8ca953d5-18b4-4eb9-bd56-18f2e5b752f0"));
    }

    #[test]
    fn test_deser_annotation() {
        let s =
r#"{
"id":192,
"description":"Some long description for a task",
"entry":"20160423T125820Z",
"modified":"20160423T125942Z",
"project":"project",
"status":"pending",
"tags":["search","things"],
"uuid":"5a04bb1e-3f4b-49fb-b9ba-44407ca223b5",
"annotations":[{"entry":"20160423T125911Z","description":"An Annotation"},
               {"entry":"20160423T125926Z","description":"Another Annotation"},
               {"entry":"20160422T125942Z","description":"A Third Anno"}
               ],
"urgency":10.911
}"#;

        println!("{}", s);

        let task = serde_json::from_str(s);
        println!("{:?}", task);
        assert!(task.is_ok());
        let task : Task = task.unwrap();

        let all_annotations = vec![
            Annotation::new(mkdate("20160423T125911Z"), String::from("An Annotation")),
            Annotation::new(mkdate("20160423T125926Z"), String::from("Another Annotation")),
            Annotation::new(mkdate("20160422T125942Z"), String::from("A Third Anno"))
        ];

        if let Some(annotations) = task.annotations() {
            for annotation in annotations {
                let r = all_annotations.iter().any(|ann| {
                    let descr = ann.description() == annotation.description();
                    let entry = ann.entry() == annotation.entry();

                    descr && entry
                });
                assert!(r, "Annotation {:?} missing or buggy", annotation);
            }
        } else {
            assert!(false, "Annotations missing");
        }
    }

}

<|endoftext|>


<commit_before>use super::{AsStr, Element, XmlReader, XmlWriter};
use super::Event::*;
use super::error::Result;
use std::str::from_utf8;
use std::io::Cursor;

macro_rules! next_eq {
    ($r: expr, $($t:path, $bytes:expr),*) => {
        $(
            match $r.next() {
                Some(Ok($t(ref e))) => {
                    assert!(e.name() == $bytes, "expecting {:?}, found {:?}",
                            from_utf8($bytes), e.content().as_str());
                },
                Some(Ok(e)) => {
                    assert!(false, "expecting {:?}, found {:?}",
                            $t(Element::from_buffer($bytes.to_vec(), 0, $bytes.len(), $bytes.len())), e);
                },
                p => {
                    assert!(false, "expecting {:?}, found {:?}",
                            $t(Element::from_buffer($bytes.to_vec(), 0, $bytes.len(), $bytes.len())), p);
                }
            }
        )*
    }
}

#[test]
fn test_start() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Start, b"a");
}

#[test]
fn test_start_end() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Start, b"a", End, b"a");
}

#[test]
fn test_start_end_attr() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Start, b"a", End, b"a");
}

#[test]
fn test_start_end_comment() {
    let mut r = XmlReader::from_str(" ").trim_text(true);
    next_eq!(r,
             Start, b"b",
             Start, b"a",
             End, b"a",
             Start, b"a",
             End, b"a",
             Comment, b"t",
             End, b"b"
            );
}

#[test]
fn test_start_txt_end() {
    let mut r = XmlReader::from_str("test").trim_text(true);
    next_eq!(r, Start, b"a", Text, b"test", End, b"a");
}

#[test]
fn test_comment() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Comment, b"test");
}

#[test]
fn test_header() {
    let mut r = XmlReader::from_str("<?header?>").trim_text(true);
    next_eq!(r, Header, b"header");
}

#[test]
fn test_trim_test() {
    let txt = "  ";
    let mut r = XmlReader::from_str(&txt).trim_text(true);
    next_eq!(r, Start, b"a",
                Start, b"b",
                End, b"b",
                End, b"a");

    let mut r = XmlReader::from_str(&txt).trim_text(false);
    next_eq!(r, Text, b"",
                Start, b"a",
                Text, b"",
                Start, b"b",
                Text, b"  ",
                End, b"b",
                Text, b"",
                End, b"a");
}

#[test]
fn test_cdata() {
    let mut r = XmlReader::from_str("<![CDATA[test]]>").trim_text(true);
    next_eq!(r, CData, b"test");
}

#[test]
fn test_cdata_open_close() {
    let mut r = XmlReader::from_str("<![CDATA[test <> test]]>").trim_text(true);
    next_eq!(r, CData, b"test <> test");
}

#[test]
fn test_start_attr() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Start, b"a");
}

#[test]
fn test_nested() {
    let mut r = XmlReader::from_str("test<c/>").trim_text(true);
    next_eq!(r,
             Start, b"a",
             Start, b"b",
             Text, b"test",
             End, b"b",
             Start, b"c",
             End, b"c",
             End, b"a"
            );
}

#[test]
fn test_writer() {
    let str = r#"<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.github.sample" android:versionName="Lollipop" android:versionCode="5.1"><application android:label="SampleApplication"></application></manifest>"#;
    let reader = XmlReader::from_str(&str).trim_text(true);
    let mut writer = XmlWriter::new(Cursor::new(Vec::new()));
    for event in reader {
        assert!(writer.write(event.unwrap()).is_ok());
    }

    let result = writer.into_inner().into_inner();
    assert_eq!(result, str.as_bytes());
}

#[test]
fn test_write_attrs() {
    let str_from = r#"<source attr="val"></source>"#;
    let expected = r#"<copy attr="val" a="b" c="d" x="y"></copy>"#;
    let reader = XmlReader::from_str(&str_from).trim_text(true);
    let mut writer = XmlWriter::new(Cursor::new(Vec::new()));
    for event in reader {
        let event = event.unwrap();
        let event = match event {
            Start(elem) => {
                let mut attrs = elem.attributes().collect::<Result<Vec<_>>>().unwrap();
                attrs.extend_from_slice(&[(b"a", b"b"), (b"c", b"d")]);
                let mut elem = Element::new("copy").with_attributes(attrs);
                elem.push_attribute("x", "y");
                Start(elem)
            },
            End(_elem) => End(Element::new("copy")),
            _ => event
        };
        assert!(writer.write(event).is_ok());
    }

    let result = writer.into_inner().into_inner();
    assert_eq!(result, expected.as_bytes());
}

<commit_msg>add test for buf position<commit_after>use super::{AsStr, Element, XmlReader, XmlWriter};
use super::Event::*;
use super::error::Result;
use std::str::from_utf8;
use std::io::Cursor;

macro_rules! next_eq {
    ($r: expr, $($t:path, $bytes:expr),*) => {
        $(
            match $r.next() {
                Some(Ok($t(ref e))) => {
                    assert!(e.name() == $bytes, "expecting {:?}, found {:?}",
                            from_utf8($bytes), e.content().as_str());
                },
                Some(Ok(e)) => {
                    assert!(false, "expecting {:?}, found {:?}",
                            $t(Element::from_buffer($bytes.to_vec(), 0, $bytes.len(), $bytes.len())), e);
                },
                Some(Err((e, pos))) => {
                    assert!(false, "{:?} at buffer position {}", e, pos);
                },
                p => {
                    assert!(false, "expecting {:?}, found {:?}",
                            $t(Element::from_buffer($bytes.to_vec(), 0, $bytes.len(), $bytes.len())), p);
                }
            }
        )*
    }
}

#[test]
fn test_start() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Start, b"a");
}

#[test]
fn test_start_end() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Start, b"a", End, b"a");
}

#[test]
fn test_start_end_attr() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Start, b"a", End, b"a");
}

#[test]
fn test_start_end_comment() {
    let mut r = XmlReader::from_str(" ").trim_text(true);
    next_eq!(r,
             Start, b"b",
             Start, b"a",
             End, b"a",
             Start, b"a",
             End, b"a",
             Comment, b"t",
             End, b"b"
            );
}

#[test]
fn test_start_txt_end() {
    let mut r = XmlReader::from_str("test").trim_text(true);
    next_eq!(r, Start, b"a", Text, b"test", End, b"a");
}

#[test]
fn test_comment() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Comment, b"test");
}

#[test]
fn test_header() {
    let mut r = XmlReader::from_str("<?header?>").trim_text(true);
    next_eq!(r, Header, b"header");
}

#[test]
fn test_trim_test() {
    let txt = "  ";
    let mut r = XmlReader::from_str(&txt).trim_text(true);
    next_eq!(r, Start, b"a",
                Start, b"b",
                End, b"b",
                End, b"a");

    let mut r = XmlReader::from_str(&txt).trim_text(false);
    next_eq!(r, Text, b"",
                Start, b"a",
                Text, b"",
                Start, b"b",
                Text, b"  ",
                End, b"b",
                Text, b"",
                End, b"a");
}

#[test]
fn test_cdata() {
    let mut r = XmlReader::from_str("<![CDATA[test]]>").trim_text(true);
    next_eq!(r, CData, b"test");
}

#[test]
fn test_cdata_open_close() {
    let mut r = XmlReader::from_str("<![CDATA[test <> test]]>").trim_text(true);
    next_eq!(r, CData, b"test <> test");
}

#[test]
fn test_start_attr() {
    let mut r = XmlReader::from_str("").trim_text(true);
    next_eq!(r, Start, b"a");
}

#[test]
fn test_nested() {
    let mut r = XmlReader::from_str("test<c/>").trim_text(true);
    next_eq!(r,
             Start, b"a",
             Start, b"b",
             Text, b"test",
             End, b"b",
             Start, b"c",
             End, b"c",
             End, b"a"
            );
}

#[test]
fn test_writer() {
    let str = r#"<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.github.sample" android:versionName="Lollipop" android:versionCode="5.1"><application android:label="SampleApplication"></application></manifest>"#;
    let reader = XmlReader::from_str(&str).trim_text(true);
    let mut writer = XmlWriter::new(Cursor::new(Vec::new()));
    for event in reader {
        assert!(writer.write(event.unwrap()).is_ok());
    }

    let result = writer.into_inner().into_inner();
    assert_eq!(result, str.as_bytes());
}

#[test]
fn test_write_attrs() {
    let str_from = r#"<source attr="val"></source>"#;
    let expected = r#"<copy attr="val" a="b" c="d" x="y"></copy>"#;
    let reader = XmlReader::from_str(&str_from).trim_text(true);
    let mut writer = XmlWriter::new(Cursor::new(Vec::new()));
    for event in reader {
        let event = event.unwrap();
        let event = match event {
            Start(elem) => {
                let mut attrs = elem.attributes().collect::<Result<Vec<_>>>().unwrap();
                attrs.extend_from_slice(&[(b"a", b"b"), (b"c", b"d")]);
                let mut elem = Element::new("copy").with_attributes(attrs);
                elem.push_attribute("x", "y");
                Start(elem)
            },
            End(_elem) => End(Element::new("copy")),
            _ => event
        };
        assert!(writer.write(event).is_ok());
    }

    let result = writer.into_inner().into_inner();
    assert_eq!(result, expected.as_bytes());
}

#[test]
fn test_buf_position() {
    let mut r = XmlReader::from_str("")
        .trim_text(true).with_check(true);
    match r.next() {
        Some(Err((_, 4))) => assert!(true),
        Some(Err((_, n))) => assert!(false, "expecting buf_pos = 4, found {}", n),
        e => assert!(false, "expecting error, found {:?}", e),
    }
}

<|endoftext|>


<commit_before>#![allow(dead_code, unused_variables)]

use {Action, InputTranslator, RebindBuilder, InputRebind, Translated};
use input::Input;
use input::Button::Keyboard;
use input::keyboard::Key;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
enum TestAction {
    Action1, Action2, Action3, Action4, Action5,
    Action6, Action7, Action8, Action9, Action10
}

impl Action for TestAction { }

type TestBuilder = RebindBuilder<TestAction>;
type TestTranslator = InputTranslator<TestAction>;
type TestRebind = InputRebind<TestAction>;

fn create_prepopulated_builder() -> TestBuilder {
    RebindBuilder::default()
        .with_action_mapping(Keyboard(Key::Up),    TestAction::Action1)
        .with_action_mapping(Keyboard(Key::W),     TestAction::Action1)
        .with_action_mapping(Keyboard(Key::Down),  TestAction::Action2)
        .with_action_mapping(Keyboard(Key::S),     TestAction::Action2)
        .with_action_mapping(Keyboard(Key::Left),  TestAction::Action3)
        .with_action_mapping(Keyboard(Key::A),     TestAction::Action3)
        .with_action_mapping(Keyboard(Key::Right), TestAction::Action4)
        .with_action_mapping(Keyboard(Key::D),     TestAction::Action4)
}

#[test]
fn test_translator_get_action_from_buttonpress() {
    let translator = create_prepopulated_builder().build_translator();

    assert_eq!(translator.translate(&Input::Press(Keyboard(Key::Down))).unwrap(),
               Translated::Press(TestAction::Action2));

    assert_eq!(translator.translate(&Input::Press(Keyboard(Key::D))).unwrap(),
               Translated::Press(TestAction::Action4));

    assert_eq!(translator.translate(&Input::Press(Keyboard(Key::Left))).unwrap(),
               Translated::Press(TestAction::Action3));

    assert_eq!(translator.translate(&Input::Press(Keyboard(Key::W))).unwrap(),
               Translated::Press(TestAction::Action1));
}

#[test]
fn test_conversion_from_rebind_to_translator() {
    let translator = create_prepopulated_builder().build_translator();

    let translator_clone = translator.clone();
    let converted_translator = Into::<TestTranslator>::into(
            Into::<TestRebind>::into(translator));

    assert_eq!(translator_clone, converted_translator);
}
<commit_msg>Add test to test adding an action binding using<commit_after>#![allow(dead_code, unused_variables)]

use {Action, ButtonTuple, InputTranslator, RebindBuilder, InputRebind, Translated};
use input::Input;
use input::Button::Keyboard;
use input::keyboard::Key;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Ord, PartialOrd)]
enum TestAction {
    Action1, Action2, Action3, Action4, Action5,
    Action6, Action7, Action8, Action9, Action10
}

impl Action for TestAction { }

type TestBuilder = RebindBuilder<TestAction>;
type TestTranslator = InputTranslator<TestAction>;
type TestRebind = InputRebind<TestAction>;

fn create_prepopulated_builder() -> TestBuilder {
    RebindBuilder::default()
        .with_action_mapping(Keyboard(Key::Up),    TestAction::Action1)
        .with_action_mapping(Keyboard(Key::W),     TestAction::Action1)
        .with_action_mapping(Keyboard(Key::Down),  TestAction::Action2)
        .with_action_mapping(Keyboard(Key::S),     TestAction::Action2)
        .with_action_mapping(Keyboard(Key::Left),  TestAction::Action3)
        .with_action_mapping(Keyboard(Key::A),     TestAction::Action3)
        .with_action_mapping(Keyboard(Key::Right), TestAction::Action4)
        .with_action_mapping(Keyboard(Key::D),     TestAction::Action4)
}

#[test]
fn test_translator_get_action_from_buttonpress() {
    let translator = create_prepopulated_builder().build_translator();

    assert_eq!(translator.translate(&Input::Press(Keyboard(Key::Down))).unwrap(),
               Translated::Press(TestAction::Action2));

    assert_eq!(translator.translate(&Input::Press(Keyboard(Key::D))).unwrap(),
               Translated::Press(TestAction::Action4));

    assert_eq!(translator.translate(&Input::Press(Keyboard(Key::Left))).unwrap(),
               Translated::Press(TestAction::Action3));

    assert_eq!(translator.translate(&Input::Press(Keyboard(Key::W))).unwrap(),
               Translated::Press(TestAction::Action1));
}

#[test]
fn test_conversion_from_rebind_to_translator() {
    let translator = create_prepopulated_builder().build_translator();

    let translator_clone = translator.clone();
    let converted_translator = Into::<TestTranslator>::into(
            Into::<TestRebind>::into(translator));

    assert_eq!(translator_clone, converted_translator);
}

#[test]
fn test_add_button_to_translator_using_rebind() {
    use input::Button;
    const Q_KEY: Button = Keyboard(Key::Q);
    const E_KEY: Button = Keyboard(Key::E);

    let translator = create_prepopulated_builder().build_translator();
    let mut rebind = translator.into_rebind();
    rebind.insert_action_with_buttons(TestAction::Action5, ButtonTuple(Some(Q_KEY), Some(E_KEY), None));

    let translator = rebind.into_translator();

    assert_eq!(translator.translate(&Input::Press(Q_KEY)), Some(Translated::Press(TestAction::Action5)));
    assert_eq!(translator.translate(&Input::Press(E_KEY)), Some(Translated::Press(TestAction::Action5)));
}
<|endoftext|>


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

use std::io;
use std::process::Command;
use std::fs;
use std::path::PathBuf;

use devicemapper::{DM, Device, DmFlags, DevId, DM_SUSPEND};
use uuid::Uuid;
use nix::sys::stat::{mknod, umask, Mode, S_IFBLK, S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP};

use types::{Sectors, DataBlocks, FroyoError, FroyoResult};
use raid::{RaidSegment, RaidLinearDev, RaidLinearDevSave};
use util::{clear_dev, setup_dm_dev, teardown_dm_dev};
use consts::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinPoolDevSave {
    pub data_block_size: Sectors,
    pub low_water_blocks: DataBlocks,
    pub meta_dev: RaidLinearDevSave,
    pub data_dev: RaidLinearDevSave,
}

#[derive(Debug, Clone)]
pub struct ThinPoolDev {
    dm_name: String,
    dev: Device,
    data_block_size: Sectors,
    low_water_blocks: DataBlocks,
    params: String,
    meta_dev: RaidLinearDev,
    data_dev: RaidLinearDev,
}

#[derive(Debug, Clone, Copy)]
pub struct ThinPoolBlockUsage {
    pub used_meta: u64,
    pub total_meta: u64,
    pub used_data: DataBlocks,
    pub total_data: DataBlocks,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinPoolStatus {
    Good((ThinPoolWorkingStatus, ThinPoolBlockUsage)),
    Fail,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinPoolWorkingStatus {
    Good,
    ReadOnly,
    OutOfSpace,
    NeedsCheck,
}

impl ThinPoolDev {
    pub fn new(dm: &DM,
               id: &str,
               meta_segs: Vec<RaidSegment>,
               data_segs: Vec<RaidSegment>)
               -> FroyoResult<ThinPoolDev> {
        // meta
        let meta_name = format!("thin-meta-{}", id);
        let meta_raid_dev = try!(RaidLinearDev::new(
            dm,
            &meta_name,
            &Uuid::new_v4().to_simple_string(),
            meta_segs));

        try!(clear_dev(meta_raid_dev.dev));

        // data
        let data_name = format!("thin-data-{}", id);
        let data_raid_dev = try!(RaidLinearDev::new(
            dm,
            &data_name,
            &Uuid::new_v4().to_simple_string(),
            data_segs));

        let data_block_size = Sectors::new(2048); // 1MiB
        let low_water_blocks = DataBlocks::new(512); // 512MiB

        ThinPoolDev::setup(
            dm,
            id,
            data_block_size,
            low_water_blocks,
            meta_raid_dev,
            data_raid_dev)
    }

    pub fn setup(
        dm: &DM,
        id: &str,
        data_block_size: Sectors,
        low_water_blocks: DataBlocks,
        meta_raid_dev: RaidLinearDev,
        data_raid_dev: RaidLinearDev)
        -> FroyoResult<ThinPoolDev> {

        let params = format!("{}:{} {}:{} {} {}",
                             meta_raid_dev.dev.major,
                             meta_raid_dev.dev.minor,
                             data_raid_dev.dev.major,
                             data_raid_dev.dev.minor,
                             *data_block_size,
                             *low_water_blocks);
        let table = [(0u64, *data_raid_dev.length(), "thin-pool", &*params)];

        let dm_name = format!("froyo-thin-pool-{}", id);
        let pool_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        Ok(ThinPoolDev {
            dm_name: dm_name,
            dev: pool_dev,
            data_block_size: data_block_size,
            low_water_blocks: low_water_blocks,
            params: params.clone(),
            meta_dev: meta_raid_dev,
            data_dev: data_raid_dev,
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        try!(teardown_dm_dev(dm, &self.dm_name));
        try!(self.meta_dev.teardown(dm));
        try!(self.data_dev.teardown(dm));

        Ok(())
    }

    pub fn to_save(&self) -> ThinPoolDevSave {
        ThinPoolDevSave {
            data_block_size: self.data_block_size,
            low_water_blocks: self.low_water_blocks,
            meta_dev: self.meta_dev.to_save(),
            data_dev: self.data_dev.to_save(),
        }
    }

    pub fn status(&self) -> FroyoResult<ThinPoolStatus> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(
            dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from thin pool status")))
        }

        let status_line = status.pop().unwrap().3;
        if status_line.starts_with("Fail") {
            return Ok(ThinPoolStatus::Fail)
        }

        let status_vals = status_line.split(' ').collect::<Vec<_>>();
        if status_vals.len() < 8 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned too few values from thin pool status")))
        }

        let usage = {
            let meta_vals = status_vals[1].split('/').collect::<Vec<_>>();
            let data_vals = status_vals[2].split('/').collect::<Vec<_>>();
            ThinPoolBlockUsage {
                used_meta: meta_vals[0].parse::<u64>().unwrap(),
                total_meta: meta_vals[1].parse::<u64>().unwrap(),
                used_data: DataBlocks::new(data_vals[0].parse::<u64>().unwrap()),
                total_data: DataBlocks::new(data_vals[1].parse::<u64>().unwrap()),
            }
        };

        match status_vals[7] {
            "-" => {},
            "needs_check" => return Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::NeedsCheck, usage))),
            _ => return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned unexpected value in thin pool status")))
        }

        match status_vals[4] {
            "rw" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::Good, usage))),
            "ro" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::ReadOnly, usage))),
            "out_of_data_space" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::OutOfSpace, usage))),
            _ => Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned unexpected value in thin pool status")))
        }
    }

    // return size of a data block in bytes
    pub fn data_block_size(&self) -> u64 {
        *self.data_block_size * SECTOR_SIZE
    }

    pub fn sectors_to_blocks(&self, sectors: Sectors) -> DataBlocks {
        DataBlocks::new(*sectors / *self.data_block_size)
    }

    pub fn blocks_to_sectors(&self, blocks: DataBlocks) -> Sectors {
        Sectors::new(*blocks * *self.data_block_size)
    }

    pub fn extend_data_dev(&mut self, segs: Vec<RaidSegment>)
                           -> FroyoResult<()> {
        try!(self.data_dev.extend(segs));
        try!(self.dm_reload());
        Ok(())
    }

    pub fn extend_meta_dev(&mut self, segs: Vec<RaidSegment>)
                           -> FroyoResult<()> {
        try!(self.meta_dev.extend(segs));
        try!(self.dm_reload());
        Ok(())
    }

    fn dm_reload(&self) -> FroyoResult<()> {
        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        let table = [(0u64, *self.data_dev.length(), "thin-pool", &*self.params)];

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));
        Ok(())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinDevSave {
    pub name: String,
    pub thin_number: u32,
    pub size: Sectors,
}

#[derive(Debug, Clone)]
pub struct ThinDev {
    dev: Device,
    name: String,
    thin_number: u32,
    pub size: Sectors,
    dm_name: String,
    params: String,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinStatus {
    Good(Sectors),
    Fail,
}

impl ThinDev {
    pub fn new(
        dm: &DM,
        froyo_id: &str,
        name: &str,
        thin_number: u32,
        size: Sectors,
        pool_dev: &ThinPoolDev)
        -> FroyoResult<ThinDev> {

        try!(dm.target_msg(&DevId::Name(&pool_dev.dm_name),
                           0, &format!("create_thin {}", thin_number)));

        let mut td = try!(ThinDev::setup(
            dm,
            froyo_id,
            name,
            thin_number,
            size,
            pool_dev));

        try!(td.create_devnode());
        try!(td.create_fs(name));

        Ok(td)
    }

    pub fn setup(
        dm: &DM,
        froyo_id: &str,
        name: &str,
        thin_number: u32,
        size: Sectors,
        pool_dev: &ThinPoolDev)
        -> FroyoResult<ThinDev> {

        let params = format!("{}:{} {}", pool_dev.dev.major,
                             pool_dev.dev.minor, thin_number);
        let table = [(0u64, *size, "thin", &*params)];

        let dm_name = format!("froyo-thin-{}-{}", froyo_id, thin_number);
        let thin_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        Ok(ThinDev {
            dev: thin_dev,
            name: name.to_owned(),
            thin_number: thin_number,
            size: size,
            dm_name: dm_name,
            params: params.clone(),
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        try!(self.remove_devnode());

        try!(teardown_dm_dev(dm, &self.dm_name));

        Ok(())
    }

    pub fn extend(&mut self, sectors: Sectors) -> FroyoResult<()> {

        self.size = self.size + sectors;

        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        let table = [(0u64, *self.size, "thin", &*self.params)];

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));

        // TODO: filesystem still needs to be told to use extra space

        Ok(())
    }

    pub fn to_save(&self) -> ThinDevSave {
        ThinDevSave {
            name: self.name.clone(),
            thin_number: self.thin_number,
            size: self.size,
        }
    }

    pub fn status(&self) -> FroyoResult<ThinStatus> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(
            dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from thin status")))
        }

        // We should either get 1 line or the kernel is broken
        let status_line = status.pop().unwrap().3;
        if status_line.starts_with("Fail") {
            return Ok(ThinStatus::Fail)
        }
        let status_vals = status_line.split(' ').collect::<Vec<_>>();

        Ok(ThinStatus::Good(Sectors::new(
            status_vals[0].parse::<u64>().unwrap())))
    }

    fn create_devnode(&mut self) -> FroyoResult<()> {
        let mut pathbuf = PathBuf::from("/dev/froyo");

        if let Err(e) = fs::create_dir(&pathbuf) {
            if e.kind() != io::ErrorKind::AlreadyExists {
                return Err(FroyoError::Io(e))
            }
        }

        pathbuf.push(&self.name);

        let old_umask = umask(Mode::empty());
        let res = mknod(&pathbuf,
                    S_IFBLK,
                    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP,
                    self.dev.into());
        umask(old_umask);
        if let Err(e) = res {
            return Err(FroyoError::Nix(e))
        }

        Ok(())
    }

    fn remove_devnode(&mut self) -> FroyoResult<()> {
        let mut pathbuf = PathBuf::from("/dev/froyo");
        pathbuf.push(&self.name);
        if let Err(_) = fs::remove_file(&pathbuf) {
            dbgp!("Could not remove device node {}", pathbuf.display());
        }

        Ok(())
    }

    fn create_fs(&mut self, name: &str) -> FroyoResult<()> {
        let dev_name = format!("/dev/froyo/{}", name);
        let output = try!(Command::new("mkfs.xfs")
                          .arg("-f")
                          .arg(&dev_name)
                          .output());

        if output.status.success(){
            dbgp!("Created xfs filesystem on {}", dev_name)
        } else {
            println!("err {}", String::from_utf8_lossy(&output.stderr));
        }
        Ok(())
    }
}
<commit_msg>Be more aggressive about creating thin device devnode<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::io;
use std::process::Command;
use std::fs;
use std::path::PathBuf;

use devicemapper::{DM, Device, DmFlags, DevId, DM_SUSPEND};
use uuid::Uuid;
use nix::sys::stat::{mknod, umask, Mode, S_IFBLK, S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP};
use nix::errno::EEXIST;

use types::{Sectors, DataBlocks, FroyoError, FroyoResult};
use raid::{RaidSegment, RaidLinearDev, RaidLinearDevSave};
use util::{clear_dev, setup_dm_dev, teardown_dm_dev};
use consts::*;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinPoolDevSave {
    pub data_block_size: Sectors,
    pub low_water_blocks: DataBlocks,
    pub meta_dev: RaidLinearDevSave,
    pub data_dev: RaidLinearDevSave,
}

#[derive(Debug, Clone)]
pub struct ThinPoolDev {
    dm_name: String,
    dev: Device,
    data_block_size: Sectors,
    low_water_blocks: DataBlocks,
    params: String,
    meta_dev: RaidLinearDev,
    data_dev: RaidLinearDev,
}

#[derive(Debug, Clone, Copy)]
pub struct ThinPoolBlockUsage {
    pub used_meta: u64,
    pub total_meta: u64,
    pub used_data: DataBlocks,
    pub total_data: DataBlocks,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinPoolStatus {
    Good((ThinPoolWorkingStatus, ThinPoolBlockUsage)),
    Fail,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinPoolWorkingStatus {
    Good,
    ReadOnly,
    OutOfSpace,
    NeedsCheck,
}

impl ThinPoolDev {
    pub fn new(dm: &DM,
               id: &str,
               meta_segs: Vec<RaidSegment>,
               data_segs: Vec<RaidSegment>)
               -> FroyoResult<ThinPoolDev> {
        // meta
        let meta_name = format!("thin-meta-{}", id);
        let meta_raid_dev = try!(RaidLinearDev::new(
            dm,
            &meta_name,
            &Uuid::new_v4().to_simple_string(),
            meta_segs));

        try!(clear_dev(meta_raid_dev.dev));

        // data
        let data_name = format!("thin-data-{}", id);
        let data_raid_dev = try!(RaidLinearDev::new(
            dm,
            &data_name,
            &Uuid::new_v4().to_simple_string(),
            data_segs));

        let data_block_size = Sectors::new(2048); // 1MiB
        let low_water_blocks = DataBlocks::new(512); // 512MiB

        ThinPoolDev::setup(
            dm,
            id,
            data_block_size,
            low_water_blocks,
            meta_raid_dev,
            data_raid_dev)
    }

    pub fn setup(
        dm: &DM,
        id: &str,
        data_block_size: Sectors,
        low_water_blocks: DataBlocks,
        meta_raid_dev: RaidLinearDev,
        data_raid_dev: RaidLinearDev)
        -> FroyoResult<ThinPoolDev> {

        let params = format!("{}:{} {}:{} {} {}",
                             meta_raid_dev.dev.major,
                             meta_raid_dev.dev.minor,
                             data_raid_dev.dev.major,
                             data_raid_dev.dev.minor,
                             *data_block_size,
                             *low_water_blocks);
        let table = [(0u64, *data_raid_dev.length(), "thin-pool", &*params)];

        let dm_name = format!("froyo-thin-pool-{}", id);
        let pool_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        Ok(ThinPoolDev {
            dm_name: dm_name,
            dev: pool_dev,
            data_block_size: data_block_size,
            low_water_blocks: low_water_blocks,
            params: params.clone(),
            meta_dev: meta_raid_dev,
            data_dev: data_raid_dev,
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        try!(teardown_dm_dev(dm, &self.dm_name));
        try!(self.meta_dev.teardown(dm));
        try!(self.data_dev.teardown(dm));

        Ok(())
    }

    pub fn to_save(&self) -> ThinPoolDevSave {
        ThinPoolDevSave {
            data_block_size: self.data_block_size,
            low_water_blocks: self.low_water_blocks,
            meta_dev: self.meta_dev.to_save(),
            data_dev: self.data_dev.to_save(),
        }
    }

    pub fn status(&self) -> FroyoResult<ThinPoolStatus> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(
            dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from thin pool status")))
        }

        let status_line = status.pop().unwrap().3;
        if status_line.starts_with("Fail") {
            return Ok(ThinPoolStatus::Fail)
        }

        let status_vals = status_line.split(' ').collect::<Vec<_>>();
        if status_vals.len() < 8 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned too few values from thin pool status")))
        }

        let usage = {
            let meta_vals = status_vals[1].split('/').collect::<Vec<_>>();
            let data_vals = status_vals[2].split('/').collect::<Vec<_>>();
            ThinPoolBlockUsage {
                used_meta: meta_vals[0].parse::<u64>().unwrap(),
                total_meta: meta_vals[1].parse::<u64>().unwrap(),
                used_data: DataBlocks::new(data_vals[0].parse::<u64>().unwrap()),
                total_data: DataBlocks::new(data_vals[1].parse::<u64>().unwrap()),
            }
        };

        match status_vals[7] {
            "-" => {},
            "needs_check" => return Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::NeedsCheck, usage))),
            _ => return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned unexpected value in thin pool status")))
        }

        match status_vals[4] {
            "rw" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::Good, usage))),
            "ro" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::ReadOnly, usage))),
            "out_of_data_space" => Ok(ThinPoolStatus::Good(
                (ThinPoolWorkingStatus::OutOfSpace, usage))),
            _ => Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Kernel returned unexpected value in thin pool status")))
        }
    }

    // return size of a data block in bytes
    pub fn data_block_size(&self) -> u64 {
        *self.data_block_size * SECTOR_SIZE
    }

    pub fn sectors_to_blocks(&self, sectors: Sectors) -> DataBlocks {
        DataBlocks::new(*sectors / *self.data_block_size)
    }

    pub fn blocks_to_sectors(&self, blocks: DataBlocks) -> Sectors {
        Sectors::new(*blocks * *self.data_block_size)
    }

    pub fn extend_data_dev(&mut self, segs: Vec<RaidSegment>)
                           -> FroyoResult<()> {
        try!(self.data_dev.extend(segs));
        try!(self.dm_reload());
        Ok(())
    }

    pub fn extend_meta_dev(&mut self, segs: Vec<RaidSegment>)
                           -> FroyoResult<()> {
        try!(self.meta_dev.extend(segs));
        try!(self.dm_reload());
        Ok(())
    }

    fn dm_reload(&self) -> FroyoResult<()> {
        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        let table = [(0u64, *self.data_dev.length(), "thin-pool", &*self.params)];

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));
        Ok(())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ThinDevSave {
    pub name: String,
    pub thin_number: u32,
    pub size: Sectors,
}

#[derive(Debug, Clone)]
pub struct ThinDev {
    dev: Device,
    name: String,
    thin_number: u32,
    pub size: Sectors,
    dm_name: String,
    params: String,
}

#[derive(Debug, Clone, Copy)]
pub enum ThinStatus {
    Good(Sectors),
    Fail,
}

impl ThinDev {
    pub fn new(
        dm: &DM,
        froyo_id: &str,
        name: &str,
        thin_number: u32,
        size: Sectors,
        pool_dev: &ThinPoolDev)
        -> FroyoResult<ThinDev> {

        try!(dm.target_msg(&DevId::Name(&pool_dev.dm_name),
                           0, &format!("create_thin {}", thin_number)));

        let mut td = try!(ThinDev::setup(
            dm,
            froyo_id,
            name,
            thin_number,
            size,
            pool_dev));

        try!(td.create_fs(name));

        Ok(td)
    }

    pub fn setup(
        dm: &DM,
        froyo_id: &str,
        name: &str,
        thin_number: u32,
        size: Sectors,
        pool_dev: &ThinPoolDev)
        -> FroyoResult<ThinDev> {

        let params = format!("{}:{} {}", pool_dev.dev.major,
                             pool_dev.dev.minor, thin_number);
        let table = [(0u64, *size, "thin", &*params)];

        let dm_name = format!("froyo-thin-{}-{}", froyo_id, thin_number);
        let thin_dev = try!(setup_dm_dev(dm, &dm_name, &table));

        try!(ThinDev::create_devnode(name, thin_dev));

        Ok(ThinDev {
            dev: thin_dev,
            name: name.to_owned(),
            thin_number: thin_number,
            size: size,
            dm_name: dm_name,
            params: params.clone(),
        })
    }

    pub fn teardown(&mut self, dm: &DM) -> FroyoResult<()> {
        try!(self.remove_devnode());

        try!(teardown_dm_dev(dm, &self.dm_name));

        Ok(())
    }

    pub fn extend(&mut self, sectors: Sectors) -> FroyoResult<()> {

        self.size = self.size + sectors;

        let dm = try!(DM::new());
        let id = &DevId::Name(&self.dm_name);

        let table = [(0u64, *self.size, "thin", &*self.params)];

        try!(dm.table_load(id, &table));
        try!(dm.device_suspend(id, DM_SUSPEND));
        try!(dm.device_suspend(id, DmFlags::empty()));

        // TODO: filesystem still needs to be told to use extra space

        Ok(())
    }

    pub fn to_save(&self) -> ThinDevSave {
        ThinDevSave {
            name: self.name.clone(),
            thin_number: self.thin_number,
            size: self.size,
        }
    }

    pub fn status(&self) -> FroyoResult<ThinStatus> {
        let dm = try!(DM::new());

        let (_, mut status) = try!(
            dm.table_status(&DevId::Name(&self.dm_name), DmFlags::empty()));

        if status.len() != 1 {
            return Err(FroyoError::Io(io::Error::new(
                io::ErrorKind::InvalidData,
                "Expected 1 line from thin status")))
        }

        // We should either get 1 line or the kernel is broken
        let status_line = status.pop().unwrap().3;
        if status_line.starts_with("Fail") {
            return Ok(ThinStatus::Fail)
        }
        let status_vals = status_line.split(' ').collect::<Vec<_>>();

        Ok(ThinStatus::Good(Sectors::new(
            status_vals[0].parse::<u64>().unwrap())))
    }

    fn create_devnode(name: &str, dev: Device) -> FroyoResult<()> {
        let mut pathbuf = PathBuf::from("/dev/froyo");

        if let Err(e) = fs::create_dir(&pathbuf) {
            if e.kind() != io::ErrorKind::AlreadyExists {
                return Err(FroyoError::Io(e))
            }
        }

        pathbuf.push(name);

        let old_umask = umask(Mode::empty());
        let res = mknod(&pathbuf,
                    S_IFBLK,
                    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP,
                    dev.into());
        umask(old_umask);
        if let Err(e) = res {
            if e.errno() != EEXIST {
                return Err(FroyoError::Nix(e))
            }
        }

        Ok(())
    }

    fn remove_devnode(&mut self) -> FroyoResult<()> {
        let mut pathbuf = PathBuf::from("/dev/froyo");
        pathbuf.push(&self.name);
        try!(fs::remove_file(&pathbuf));

        Ok(())
    }

    fn create_fs(&mut self, name: &str) -> FroyoResult<()> {
        let dev_name = format!("/dev/froyo/{}", name);
        let output = try!(Command::new("mkfs.xfs")
                          .arg("-f")
                          .arg(&dev_name)
                          .output());

        if output.status.success(){
            dbgp!("Created xfs filesystem on {}", dev_name)
        } else {
            println!("err {}", String::from_utf8_lossy(&output.stderr));
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>/// unit creates a type representing a single unit.
///
/// # Examples
///
/// ```
/// #[macro_use(unit)]
/// extern crate fiz_math;
/// use std::fmt::Debug;
///
/// // GoldCoins are golden!
/// unit!(GoldCoins);
///
/// // SilverCoins are like gold coins, only worse!
/// unit!(SilverCoins);
///
/// fn collect<T: Debug>(coins: GoldCoins<T>) {
///     println!("We've collected {:?} golden coins!", coins.0);
/// }
///
/// fn main() {
///     let x = GoldCoins(5);
///     collect(x);
///
///     let y = SilverCoins(6);
///
///     // Try uncommenting the line below and see:
///     //
///     // error: mismatched types: expected `GoldCoins<_>`, found `SilverCoins<_>`"
///     //
///     //collect(y);
///
///     // add, subtract, multiply, divide:
///     assert_eq!(y + SilverCoins(4), SilverCoins(10));
///     assert_eq!(y - SilverCoins(3), SilverCoins(3));
///     assert_eq!(y * SilverCoins(3), SilverCoins(18));
///     assert_eq!(y / SilverCoins(2), SilverCoins(3));
///
///     // ordering and equality:
///     assert!(y < SilverCoins(7));
///     assert!(y > SilverCoins(5));
///     assert!(y <= SilverCoins(6));
///     assert!(y >= SilverCoins(6));
///     assert!(y == SilverCoins(6));
/// }
/// ```
///
#[macro_export]
macro_rules! unit {
    ( $ident:ident ) => {
        #[derive(Copy, Clone, Debug)]
        pub struct $ident<T>(pub T);

        // addition
        impl<T: ::std::ops::Add<Output = T>> ::std::ops::Add for $ident<T> {
            type Output = Self;

            fn add(self, _rhs: Self) -> Self {
                $ident(self.0 + _rhs.0)
            }
        }

        // subtraction
        impl<T: ::std::ops::Sub<Output = T>> ::std::ops::Sub for $ident<T> {
            type Output = Self;

            fn sub(self, _rhs: Self) -> Self {
                $ident(self.0 - _rhs.0)
            }
        }

        // multiplication
        impl<T: ::std::ops::Mul<Output = T>> ::std::ops::Mul for $ident<T> {
            type Output = Self;

            fn mul(self, _rhs: Self) -> Self {
                $ident(self.0 * _rhs.0)
            }
        }

        // division
        impl<T: ::std::ops::Div<Output = T>> ::std::ops::Div for $ident<T> {
            type Output = Self;

            fn div(self, _rhs: Self) -> Self {
                $ident(self.0 / _rhs.0)
            }
        }

        // remainder
        impl<T: ::std::ops::Rem<Output = T>> ::std::ops::Rem for $ident<T> {
            type Output = Self;

            fn rem(self, rhs: Self) -> Self { $ident(self.0 % rhs.0) }
        }

        // negation
        impl<T: ::std::ops::Neg<Output = T>> ::std::ops::Neg for $ident<T> {
            type Output = Self;

            fn neg(self) -> Self::Output { $ident(-self.0) }
        }

        // not
        impl<T: ::std::ops::Not<Output = T>> ::std::ops::Not for $ident<T> {
            type Output = Self;

            fn not(self) -> Self::Output { $ident(!self.0) }
        }

        // bitwise and
        impl<T: ::std::ops::BitAnd<Output = T>> ::std::ops::BitAnd for $ident<T> {
            type Output = Self;

            fn bitand(self, rhs: Self) -> Self::Output { $ident(self.0 & rhs.0) }
        }

        // bitwise or
        impl<T: ::std::ops::BitOr<Output = T>> ::std::ops::BitOr for $ident<T> {
            type Output = Self;

            fn bitor(self, rhs: Self) -> Self::Output { $ident(self.0 | rhs.0) }
        }

        // bitwise xor
        impl<T: ::std::ops::BitXor<Output = T>> ::std::ops::BitXor for $ident<T> {
            type Output = Self;

            fn bitxor(self, rhs: Self) -> Self::Output { $ident(self.0 ^ rhs.0) }
        }

        // partial equality
        impl<T: ::std::cmp::PartialEq> ::std::cmp::PartialEq for $ident<T> {
            fn eq(&self, _rhs: &Self) -> bool {
                self.0 == _rhs.0
            }
        }

        // equality
        impl<T: ::std::cmp::Eq> ::std::cmp::Eq for $ident<T> {}

        // partial ordering
        impl<T: ::std::cmp::PartialOrd> ::std::cmp::PartialOrd for $ident<T> {
            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
                if self.0 < other.0 {
                    Some(::std::cmp::Ordering::Less)
                } else if self.0 > other.0 {
                    Some(::std::cmp::Ordering::Greater)
                } else if self == other {
                    Some(::std::cmp::Ordering::Equal)
                } else {
                    None
                }
            }
        }

        // total ordering
        impl<T: ::std::cmp::Ord> ::std::cmp::Ord for $ident<T> {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                if self.0 < other.0 {
                    ::std::cmp::Ordering::Less
                } else if self.0 > other.0 {
                    ::std::cmp::Ordering::Greater
                } else {
                    ::std::cmp::Ordering::Equal
                }
            }
        }
    };
}
<commit_msg>unit! implement shift left operator<commit_after>/// unit creates a type representing a single unit.
///
/// # Examples
///
/// ```
/// #[macro_use(unit)]
/// extern crate fiz_math;
/// use std::fmt::Debug;
///
/// // GoldCoins are golden!
/// unit!(GoldCoins);
///
/// // SilverCoins are like gold coins, only worse!
/// unit!(SilverCoins);
///
/// fn collect<T: Debug>(coins: GoldCoins<T>) {
///     println!("We've collected {:?} golden coins!", coins.0);
/// }
///
/// fn main() {
///     let x = GoldCoins(5);
///     collect(x);
///
///     let y = SilverCoins(6);
///
///     // Try uncommenting the line below and see:
///     //
///     // error: mismatched types: expected `GoldCoins<_>`, found `SilverCoins<_>`"
///     //
///     //collect(y);
///
///     // add, subtract, multiply, divide:
///     assert_eq!(y + SilverCoins(4), SilverCoins(10));
///     assert_eq!(y - SilverCoins(3), SilverCoins(3));
///     assert_eq!(y * SilverCoins(3), SilverCoins(18));
///     assert_eq!(y / SilverCoins(2), SilverCoins(3));
///
///     // ordering and equality:
///     assert!(y < SilverCoins(7));
///     assert!(y > SilverCoins(5));
///     assert!(y <= SilverCoins(6));
///     assert!(y >= SilverCoins(6));
///     assert!(y == SilverCoins(6));
/// }
/// ```
///
#[macro_export]
macro_rules! unit {
    ( $ident:ident ) => {
        #[derive(Copy, Clone, Debug)]
        pub struct $ident<T>(pub T);

        // addition
        impl<T: ::std::ops::Add<Output = T>> ::std::ops::Add for $ident<T> {
            type Output = Self;

            fn add(self, _rhs: Self) -> Self {
                $ident(self.0 + _rhs.0)
            }
        }

        // subtraction
        impl<T: ::std::ops::Sub<Output = T>> ::std::ops::Sub for $ident<T> {
            type Output = Self;

            fn sub(self, _rhs: Self) -> Self {
                $ident(self.0 - _rhs.0)
            }
        }

        // multiplication
        impl<T: ::std::ops::Mul<Output = T>> ::std::ops::Mul for $ident<T> {
            type Output = Self;

            fn mul(self, _rhs: Self) -> Self {
                $ident(self.0 * _rhs.0)
            }
        }

        // division
        impl<T: ::std::ops::Div<Output = T>> ::std::ops::Div for $ident<T> {
            type Output = Self;

            fn div(self, _rhs: Self) -> Self {
                $ident(self.0 / _rhs.0)
            }
        }

        // remainder
        impl<T: ::std::ops::Rem<Output = T>> ::std::ops::Rem for $ident<T> {
            type Output = Self;

            fn rem(self, rhs: Self) -> Self { $ident(self.0 % rhs.0) }
        }

        // negation
        impl<T: ::std::ops::Neg<Output = T>> ::std::ops::Neg for $ident<T> {
            type Output = Self;

            fn neg(self) -> Self::Output { $ident(-self.0) }
        }

        // not
        impl<T: ::std::ops::Not<Output = T>> ::std::ops::Not for $ident<T> {
            type Output = Self;

            fn not(self) -> Self::Output { $ident(!self.0) }
        }

        // bitwise and
        impl<T: ::std::ops::BitAnd<Output = T>> ::std::ops::BitAnd for $ident<T> {
            type Output = Self;

            fn bitand(self, rhs: Self) -> Self::Output { $ident(self.0 & rhs.0) }
        }

        // bitwise or
        impl<T: ::std::ops::BitOr<Output = T>> ::std::ops::BitOr for $ident<T> {
            type Output = Self;

            fn bitor(self, rhs: Self) -> Self::Output { $ident(self.0 | rhs.0) }
        }

        // bitwise xor
        impl<T: ::std::ops::BitXor<Output = T>> ::std::ops::BitXor for $ident<T> {
            type Output = Self;

            fn bitxor(self, rhs: Self) -> Self::Output { $ident(self.0 ^ rhs.0) }
        }

        // shift left
        impl<T: ::std::ops::Shl<usize>> ::std::ops::Shl<usize> for $ident<T> {
            type Output = $ident<T::Output>;

            fn shl(self, rhs: usize) -> Self::Output { $ident(self.0 << rhs) }
        }

        // partial equality
        impl<T: ::std::cmp::PartialEq> ::std::cmp::PartialEq for $ident<T> {
            fn eq(&self, _rhs: &Self) -> bool {
                self.0 == _rhs.0
            }
        }

        // equality
        impl<T: ::std::cmp::Eq> ::std::cmp::Eq for $ident<T> {}

        // partial ordering
        impl<T: ::std::cmp::PartialOrd> ::std::cmp::PartialOrd for $ident<T> {
            fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> {
                if self.0 < other.0 {
                    Some(::std::cmp::Ordering::Less)
                } else if self.0 > other.0 {
                    Some(::std::cmp::Ordering::Greater)
                } else if self == other {
                    Some(::std::cmp::Ordering::Equal)
                } else {
                    None
                }
            }
        }

        // total ordering
        impl<T: ::std::cmp::Ord> ::std::cmp::Ord for $ident<T> {
            fn cmp(&self, other: &Self) -> ::std::cmp::Ordering {
                if self.0 < other.0 {
                    ::std::cmp::Ordering::Less
                } else if self.0 > other.0 {
                    ::std::cmp::Ordering::Greater
                } else {
                    ::std::cmp::Ordering::Equal
                }
            }
        }
    };
}
<|endoftext|>


<commit_before>use serde::de::{Deserialize, DeserializeOwned, IntoDeserializer};
use serde_json::Value;

use crate::TryFromRaw;

pub fn try_convert_variant<Enum: TryFromRaw, Content: TryFromRaw>(
    variant: fn(Content) -> Enum,
    raw: Content::Raw,
) -> Result<Enum, String> {
    Content::try_from_raw(raw)
        .map(variant)
        .map_err(|err| err.to_string())
}

pub fn try_variant_from_value<T, U, E>(value: Value, variant: fn(T) -> U) -> Result<U, E>
where
    T: DeserializeOwned,
    E: serde::de::Error,
{
    serde_json::from_value(value)
        .map(variant)
        .map_err(serde_json_error_to_generic_de_error)
}

pub fn serde_json_error_to_generic_de_error<E: serde::de::Error>(error: serde_json::Error) -> E {
    E::custom(error.to_string())
}

pub fn get_field<T, E>(value: &Value, field: &'static str) -> Result<T, E>
where
    T: DeserializeOwned,
    E: serde::de::Error,
{
    serde_json::from_value(
        value
            .get(field)
            .cloned()
            .ok_or_else(|| E::missing_field(field))?,
    )
    .map_err(serde_json_error_to_generic_de_error)
}

/// Serde deserialization decorator to map empty Strings to None,
/// and forward non-empty Strings to the Deserialize implementation for T.
/// Useful for the typical
/// "A room with an X event with an absent, null, or empty Y field
/// should be treated the same as a room with no such event."
/// formulation in the spec.
///
/// To be used like this:
/// `#[serde(deserialize_with = "empty_string_as_none"]`
/// Relevant serde issue: https://github.com/serde-rs/serde/issues/1425
pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
    D: serde::Deserializer<'de>,
    T: serde::Deserialize<'de>,
{
    let opt = Option::<String>::deserialize(de)?;
    // TODO: Switch to and remove this attribute `opt.as_deref()` once MSRV is >= 1.40
    #[allow(clippy::clippy::option_as_ref_deref)]
    let opt = opt.as_ref().map(String::as_str);
    match opt {
        None | Some("") => Ok(None),
        // If T = String, like in m.room.name, the second deserialize is actually superfluous.
        // TODO: optimize that somehow?
        Some(s) => T::deserialize(s.into_deserializer()).map(Some),
    }
}

/// Serde serialization and deserialization functions that map a `Vec<T>` to a `HashMap<T, Empty>`.
///
/// The Matrix spec sometimes specifies lists as hash maps so the list entries can be expanded with
/// attributes without breaking compatibility. As that would be a breaking change for ruma's event
/// types anyway, we convert them to `Vec`s for simplicity, using this module.
///
/// To be used as `#[serde(with = "vec_as_map_of_empty")]`.
pub mod vec_as_map_of_empty {
    use crate::Empty;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};
    use std::{collections::HashMap, hash::Hash};

    #[allow(clippy::ptr_arg)]
    pub fn serialize<S, T>(vec: &Vec<T>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
        T: Serialize + Hash + Eq,
    {
        vec.iter()
            .map(|v| (v, Empty))
            .collect::<HashMap<_, _>>()
            .serialize(serializer)
    }

    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
    where
        D: Deserializer<'de>,
        T: Deserialize<'de> + Hash + Eq,
    {
        HashMap::<T, Empty>::deserialize(deserializer)
            .map(|hashmap| hashmap.into_iter().map(|(k, _)| k).collect())
    }
}

/// Used to default the `bool` fields to `true` during deserialization.
pub fn default_true() -> bool {
    true
}
<commit_msg>Fix typo<commit_after>use serde::de::{Deserialize, DeserializeOwned, IntoDeserializer};
use serde_json::Value;

use crate::TryFromRaw;

pub fn try_convert_variant<Enum: TryFromRaw, Content: TryFromRaw>(
    variant: fn(Content) -> Enum,
    raw: Content::Raw,
) -> Result<Enum, String> {
    Content::try_from_raw(raw)
        .map(variant)
        .map_err(|err| err.to_string())
}

pub fn try_variant_from_value<T, U, E>(value: Value, variant: fn(T) -> U) -> Result<U, E>
where
    T: DeserializeOwned,
    E: serde::de::Error,
{
    serde_json::from_value(value)
        .map(variant)
        .map_err(serde_json_error_to_generic_de_error)
}

pub fn serde_json_error_to_generic_de_error<E: serde::de::Error>(error: serde_json::Error) -> E {
    E::custom(error.to_string())
}

pub fn get_field<T, E>(value: &Value, field: &'static str) -> Result<T, E>
where
    T: DeserializeOwned,
    E: serde::de::Error,
{
    serde_json::from_value(
        value
            .get(field)
            .cloned()
            .ok_or_else(|| E::missing_field(field))?,
    )
    .map_err(serde_json_error_to_generic_de_error)
}

/// Serde deserialization decorator to map empty Strings to None,
/// and forward non-empty Strings to the Deserialize implementation for T.
/// Useful for the typical
/// "A room with an X event with an absent, null, or empty Y field
/// should be treated the same as a room with no such event."
/// formulation in the spec.
///
/// To be used like this:
/// `#[serde(deserialize_with = "empty_string_as_none"]`
/// Relevant serde issue: https://github.com/serde-rs/serde/issues/1425
pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
    D: serde::Deserializer<'de>,
    T: serde::Deserialize<'de>,
{
    let opt = Option::<String>::deserialize(de)?;
    // TODO: Switch to and remove this attribute `opt.as_deref()` once MSRV is >= 1.40
    #[allow(clippy::option_as_ref_deref)]
    let opt = opt.as_ref().map(String::as_str);
    match opt {
        None | Some("") => Ok(None),
        // If T = String, like in m.room.name, the second deserialize is actually superfluous.
        // TODO: optimize that somehow?
        Some(s) => T::deserialize(s.into_deserializer()).map(Some),
    }
}

/// Serde serialization and deserialization functions that map a `Vec<T>` to a `HashMap<T, Empty>`.
///
/// The Matrix spec sometimes specifies lists as hash maps so the list entries can be expanded with
/// attributes without breaking compatibility. As that would be a breaking change for ruma's event
/// types anyway, we convert them to `Vec`s for simplicity, using this module.
///
/// To be used as `#[serde(with = "vec_as_map_of_empty")]`.
pub mod vec_as_map_of_empty {
    use crate::Empty;
    use serde::{Deserialize, Deserializer, Serialize, Serializer};
    use std::{collections::HashMap, hash::Hash};

    #[allow(clippy::ptr_arg)]
    pub fn serialize<S, T>(vec: &Vec<T>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
        T: Serialize + Hash + Eq,
    {
        vec.iter()
            .map(|v| (v, Empty))
            .collect::<HashMap<_, _>>()
            .serialize(serializer)
    }

    pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
    where
        D: Deserializer<'de>,
        T: Deserialize<'de> + Hash + Eq,
    {
        HashMap::<T, Empty>::deserialize(deserializer)
            .map(|hashmap| hashmap.into_iter().map(|(k, _)| k).collect())
    }
}

/// Used to default the `bool` fields to `true` during deserialization.
pub fn default_true() -> bool {
    true
}
<|endoftext|>


<commit_before>use crate::types::*;
use async_trait::async_trait;
use ethereum_types::H256;
use generic_array::GenericArray;
use hmac::{Hmac, Mac, NewMac};
use k256::{ecdsa::VerifyKey, EncodedPoint};
use sha2::Sha256;
use sha3::{Digest, Keccak256};
use std::{future::Future, pin::Pin, time::Duration};
use tokio::{prelude::*, stream::Stream};

/// Common trait that various runtimes should implement.
#[async_trait]
pub trait Runtime {
    /// Runtime's TCP stream type.
    type TcpStream: AsyncRead + AsyncWrite + Unpin + Send + 'static;
    type TcpServer: Stream<Item = Self::TcpStream> + Unpin + Send + 'static;

    fn spawn(&self, fut: Pin<Box<dyn Future<Output = ()> + Send + 'static>>);
    async fn sleep(&self, duration: Duration);
    async fn connect_tcp(&self, target: String) -> io::Result<Self::TcpStream>;
    async fn tcp_server(&self, addr: String) -> io::Result<Self::TcpServer>;
}

pub fn keccak256(data: &[u8]) -> H256 {
    H256::from(Keccak256::digest(data).as_ref())
}

pub fn sha256(data: &[u8]) -> H256 {
    H256::from(Sha256::digest(data).as_ref())
}

pub fn hmac_sha256(key: &[u8], input: &[u8]) -> H256 {
    let mut hmac = Hmac::<Sha256>::new_varkey(key).unwrap();
    hmac.update(input);
    H256::from_slice(&*hmac.finalize().into_bytes())
}

pub fn pk2id(pk: &VerifyKey) -> PeerId {
    PeerId::from_slice(&*EncodedPoint::from(pk).to_untagged_bytes().unwrap())
}

pub fn id2pk(id: PeerId) -> Result<VerifyKey, signature::Error> {
    VerifyKey::from_encoded_point(&EncodedPoint::from_untagged_bytes(
        GenericArray::from_slice(id.as_ref()),
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    use k256::ecdsa::SigningKey;
    use rand::rngs::OsRng;

    #[test]
    fn pk2id2pk() {
        let prikey = SigningKey::random(&mut OsRng);
        let pubkey = prikey.verify_key();
        assert_eq!(pubkey, id2pk(pk2id(&pubkey)).unwrap());
    }
}
<commit_msg>Remove runtime trait<commit_after>use crate::types::*;
use ethereum_types::H256;
use generic_array::GenericArray;
use hmac::{Hmac, Mac, NewMac};
use k256::{ecdsa::VerifyKey, EncodedPoint};
use sha2::Sha256;
use sha3::{Digest, Keccak256};

pub fn keccak256(data: &[u8]) -> H256 {
    H256::from(Keccak256::digest(data).as_ref())
}

pub fn sha256(data: &[u8]) -> H256 {
    H256::from(Sha256::digest(data).as_ref())
}

pub fn hmac_sha256(key: &[u8], input: &[u8]) -> H256 {
    let mut hmac = Hmac::<Sha256>::new_varkey(key).unwrap();
    hmac.update(input);
    H256::from_slice(&*hmac.finalize().into_bytes())
}

pub fn pk2id(pk: &VerifyKey) -> PeerId {
    PeerId::from_slice(&*EncodedPoint::from(pk).to_untagged_bytes().unwrap())
}

pub fn id2pk(id: PeerId) -> Result<VerifyKey, signature::Error> {
    VerifyKey::from_encoded_point(&EncodedPoint::from_untagged_bytes(
        GenericArray::from_slice(id.as_ref()),
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    use k256::ecdsa::SigningKey;
    use rand::rngs::OsRng;

    #[test]
    fn pk2id2pk() {
        let prikey = SigningKey::random(&mut OsRng);
        let pubkey = prikey.verify_key();
        assert_eq!(pubkey, id2pk(pk2id(&pubkey)).unwrap());
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::iter::FromIterator;
use euclid::{Point2D, Rect};
use calx_grid::{FovValue, HexFov};
use world::{Location, World};
use world::query;

/// Useful general constant for cell dimension ops.
pub static PIXEL_UNIT: f32 = 16.0;

/// Transform from chart space (unit is one map cell) to view space (unit is
/// one pixel).
pub fn chart_to_view(chart_pos: Point2D<i32>) -> Point2D<f32> {
    Point2D::new((chart_pos.x as f32 * PIXEL_UNIT - chart_pos.y as f32 * PIXEL_UNIT),
                 (chart_pos.x as f32 * PIXEL_UNIT / 2.0 + chart_pos.y as f32 * PIXEL_UNIT / 2.0))
}

/// Transform from view space (unit is one pixel) to chart space (unit is one
/// map cell).
pub fn view_to_chart(view_pos: Point2D<f32>) -> Point2D<i32> {
    let c = PIXEL_UNIT / 2.0;
    let column = ((view_pos.x + c) / (c * 2.0)).floor();
    let row = ((view_pos.y - column * c) / (c * 2.0)).floor();
    Point2D::new((column + row) as i32, row as i32)
}

#[derive(Clone)]
struct ScreenFov<'a> {
    w: &'a World,
    screen_area: Rect<f32>,
    origins: Vec<Location>,
}

impl<'a> PartialEq for ScreenFov<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.w as *const World == other.w as *const World &&
        self.screen_area == other.screen_area && self.origins == other.origins
    }
}

impl<'a> Eq for ScreenFov<'a> {}

impl<'a> FovValue for ScreenFov<'a> {
    fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
        if !self.screen_area.contains(&chart_to_view(offset)) {
            return None;
        }

        let loc = self.origins[0] + offset;

        let mut ret = self.clone();
        // Go through a portal if terrain on our side of the portal is a void cell.
        //
        // With non-void terrain on top of the portal, just show our side and stay on the current
        // frame as far as FOV is concerned.
        if let Some(dest) = query::visible_portal(self.w, loc) {
            ret.origins.insert(0, dest - offset);
        }

        Some(ret)
    }
}

/// Return the field of view chart for drawing a screen.
pub fn screen_fov(
    w: &World,
    origin: Location,
    screen_area: Rect<f32>
) -> HashMap<Point2D<i32>, Vec<Location>> {
    let init = ScreenFov {
        w: w,
        screen_area: screen_area,
        origins: vec![origin],
    };

    HashMap::from_iter(HexFov::new(init).map(|(pos, a)| (pos, a.origins)))
}

#[cfg(test)]
mod test {
    use euclid::{Point2D, Rect, Size2D};
    use world::{Location, Portal, World};
    use super::screen_fov;

    fn test_world() -> World {
        use world::terrain::{Form, Kind, Tile};
        use calx_resource::ResourceStore;
        use world::Brush;

        Brush::insert_resource("dummy".to_string(), Brush::new(Vec::new()));

        Tile::insert_resource(0, Tile::new("dummy", Kind::Block, Form::Void));
        Tile::insert_resource(1, Tile::new("dummy", Kind::Ground, Form::Gate));
        Tile::insert_resource(2, Tile::new("dummy", Kind::Ground, Form::Floor));
        Tile::insert_resource(3, Tile::new("dummy", Kind::Ground, Form::Floor));

        let mut ret = World::new(1);

        ret.terrain.set(Location::new(10, 10, 0), 2);
        ret.terrain.set(Location::new(11, 11, 0), 2);
        ret.terrain.set(Location::new(9, 9, 0), 2);
        ret.terrain.set(Location::new(10, 11, 0), 2);
        ret.terrain.set(Location::new(9, 10, 0), 2);
        ret.terrain.set(Location::new(10, 9, 0), 2);

        // Void for the see-through portal.
        ret.terrain.set(Location::new(11, 10, 0), 0);
        ret.set_portal(Location::new(11, 10, 0),
                       Portal::new(Location::new(11, 10, 0), Location::new(31, 10, 0)));
        ret.terrain.set(Location::new(31, 10, 0), 3);

        ret
    }

    #[test]
    fn test_portaling_fov() {
        let world = test_world();
        let fov = screen_fov(&world,
                             Location::new(10, 10, 0),
                             Rect::new(Point2D::new(-48.0, -48.0), Size2D::new(96.0, 96.0)));
        assert_eq!(fov.get(&Point2D::new(0, 0)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&Point2D::new(0, 1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&Point2D::new(1, 1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&Point2D::new(-1, -1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&Point2D::new(1, 0)),
                   Some(&vec![Location::new(30, 10, 0), Location::new(10, 10, 0)]));
    }
}
<commit_msg>Add test for screen-fov corner visibility<commit_after>use std::collections::HashMap;
use std::iter::FromIterator;
use euclid::{Point2D, Rect};
use calx_grid::{FovValue, HexFov};
use world::{Location, World};
use world::query;

/// Useful general constant for cell dimension ops.
pub static PIXEL_UNIT: f32 = 16.0;

/// Transform from chart space (unit is one map cell) to view space (unit is
/// one pixel).
pub fn chart_to_view(chart_pos: Point2D<i32>) -> Point2D<f32> {
    Point2D::new((chart_pos.x as f32 * PIXEL_UNIT - chart_pos.y as f32 * PIXEL_UNIT),
                 (chart_pos.x as f32 * PIXEL_UNIT / 2.0 + chart_pos.y as f32 * PIXEL_UNIT / 2.0))
}

/// Transform from view space (unit is one pixel) to chart space (unit is one
/// map cell).
pub fn view_to_chart(view_pos: Point2D<f32>) -> Point2D<i32> {
    let c = PIXEL_UNIT / 2.0;
    let column = ((view_pos.x + c) / (c * 2.0)).floor();
    let row = ((view_pos.y - column * c) / (c * 2.0)).floor();
    Point2D::new((column + row) as i32, row as i32)
}

#[derive(Clone)]
struct ScreenFov<'a> {
    w: &'a World,
    screen_area: Rect<f32>,
    origins: Vec<Location>,
}

impl<'a> PartialEq for ScreenFov<'a> {
    fn eq(&self, other: &Self) -> bool {
        self.w as *const World == other.w as *const World &&
        self.screen_area == other.screen_area && self.origins == other.origins
    }
}

impl<'a> Eq for ScreenFov<'a> {}

impl<'a> FovValue for ScreenFov<'a> {
    fn advance(&self, offset: Point2D<i32>) -> Option<Self> {
        if !self.screen_area.contains(&chart_to_view(offset)) {
            return None;
        }

        let loc = self.origins[0] + offset;

        let mut ret = self.clone();
        // Go through a portal if terrain on our side of the portal is a void cell.
        //
        // With non-void terrain on top of the portal, just show our side and stay on the current
        // frame as far as FOV is concerned.
        if let Some(dest) = query::visible_portal(self.w, loc) {
            ret.origins.insert(0, dest - offset);
        }

        Some(ret)
    }
}

/// Return the field of view chart for drawing a screen.
pub fn screen_fov(
    w: &World,
    origin: Location,
    screen_area: Rect<f32>
) -> HashMap<Point2D<i32>, Vec<Location>> {
    let init = ScreenFov {
        w: w,
        screen_area: screen_area,
        origins: vec![origin],
    };

    HashMap::from_iter(HexFov::new(init).map(|(pos, a)| (pos, a.origins)))
}

#[cfg(test)]
mod test {
    use euclid::{Point2D, Rect, Size2D};
    use world::{Location, Portal, World};
    use super::{view_to_chart, screen_fov};

    fn test_world() -> World {
        use world::terrain::{Form, Kind, Tile};
        use calx_resource::ResourceStore;
        use world::Brush;

        Brush::insert_resource("dummy".to_string(), Brush::new(Vec::new()));

        Tile::insert_resource(0, Tile::new("dummy", Kind::Block, Form::Void));
        Tile::insert_resource(1, Tile::new("dummy", Kind::Ground, Form::Gate));
        Tile::insert_resource(2, Tile::new("dummy", Kind::Ground, Form::Floor));
        Tile::insert_resource(3, Tile::new("dummy", Kind::Ground, Form::Floor));

        let mut ret = World::new(1);

        ret.terrain.set(Location::new(10, 10, 0), 2);
        ret.terrain.set(Location::new(11, 11, 0), 2);
        ret.terrain.set(Location::new(9, 9, 0), 2);
        ret.terrain.set(Location::new(10, 11, 0), 2);
        ret.terrain.set(Location::new(9, 10, 0), 2);
        ret.terrain.set(Location::new(10, 9, 0), 2);

        // Void for the see-through portal.
        ret.terrain.set(Location::new(11, 10, 0), 0);
        ret.set_portal(Location::new(11, 10, 0),
                       Portal::new(Location::new(11, 10, 0), Location::new(31, 10, 0)));
        ret.terrain.set(Location::new(31, 10, 0), 3);

        ret
    }

    #[test]
    fn test_portaling_fov() {
        let world = test_world();
        let fov = screen_fov(&world,
                             Location::new(10, 10, 0),
                             Rect::new(Point2D::new(-48.0, -48.0), Size2D::new(96.0, 96.0)));
        assert_eq!(fov.get(&Point2D::new(0, 0)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&Point2D::new(0, 1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&Point2D::new(1, 1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&Point2D::new(-1, -1)),
                   Some(&vec![Location::new(10, 10, 0)]));

        assert_eq!(fov.get(&Point2D::new(1, 0)),
                   Some(&vec![Location::new(30, 10, 0), Location::new(10, 10, 0)]));
    }

    #[test]
    fn test_corner_visibility() {
        let world = test_world();
        let fov = screen_fov(&world,
                             Location::new(10, 10, 0),
                             Rect::new(Point2D::new(-200.0, -200.0), Size2D::new(400.0, 400.0)));
        let upper_left_chart = view_to_chart(Point2D::new(-180.0, -180.0));
        assert!(fov.get(&upper_left_chart).is_some());
    }
}
<|endoftext|>