<commit_before>use business::db::Db;
use entities::*;
use rusted_cypher::GraphClient;
use business::error::RepoError;
use std::result;

type Result<T> = result::Result<T, RepoError>;

impl Db for GraphClient {

    fn get_entry(&self, id: &str) -> Result<Entry> {
        let result = self.exec(cypher_stmt!(
        "MATCH (e:Entry)<--(s:EntryState) WHERE e.id = {id}
         WITH max(s.version) as version
         MATCH (e:Entry)<--(s:EntryState)
         WHERE e.id = {id} AND s.version = version
         WITH e, s
         MATCH s<-[:BELONGS_TO]-(c:Category)
         WITH e, s, collect(DISTINCT c.id) as categories
         RETURN {
           id          : e.id,
           created     : s.created,
           version     : s.version,
           title       : s.title,
           description : s.description,
           lat         : s.lat,
           lng         : s.lng,
           street      : s.street,
           zip         : s.zip,
           city        : s.city,
           country     : s.country,
           email       : s.email,
           telephone   : s.telephone,
           homepage    : s.homepage,
           categories  : categories,
           license     : s.license
         } AS e
         ORDER BY e.created DESC", {"id" => &id})?)?;
        let r = result.rows().next().ok_or(RepoError::NotFound)?;
        let e = r.get::<Entry>("e")?;
        Ok(e)
    }

    fn get_user(&self, username: &str) -> Result<User> {
        let result = self.exec(cypher_stmt!(
        "MATCH u:User
         WHERE u.username = {username}",
        { "username" => username })?)?;
        let r = result.rows().next().ok_or(RepoError::NotFound)?;
        let u = r.get::<User>("u")?;
        Ok(u)
    }

    fn all_entries(&self) -> Result<Vec<Entry>> {
        let result = self.exec(
        "MATCH (e:Entry)<--(x:EntryState)
          WITH distinct e, max(x.created) as max
          MATCH e<--(s:EntryState)
          WHERE s.created = max
          WITH e,s
          MATCH e<-[:BELONGS_TO]-s
          OPTIONAL MATCH (s)<-[:BELONGS_TO]-(c:Category)
          WITH e, s, collect(DISTINCT c.id) as categories
          RETURN {
            id          : e.id,
            created     : s.created,
            version     : s.version,
            title       : s.title,
            description : s.description,
            lat         : s.lat,
            lng         : s.lng,
            street      : s.street,
            zip         : s.zip,
            city        : s.city,
            country     : s.country,
            email       : s.email,
            telephone   : s.telephone,
            homepage    : s.homepage,
            categories  : categories,
            license     : s.license
          } AS e
          ORDER BY e.created DESC")?;
        Ok(result.rows()
            .filter_map(|r| r.get::<Entry>("e").ok())
            .collect::<Vec<Entry>>())
    }

    fn create_entry(&mut self, e: &Entry) -> Result<()> {
        self.exec(cypher_stmt!(
        "MATCH (c:Category)
         WHERE c.id in {categories}
         WITH
            collect(DISTINCT c)    AS cats,
            collect(DISTINCT c.id) AS cat_ids,
            count(c)               AS cnt
         WHERE cnt > 0
         CREATE (e:Entry {id:{id}})
         MERGE e<-[:BELONGS_TO]-(s:EntryState {
           created : timestamp(),
           version : 1
         })
         SET s.title       = {title},
             s.description = {description},
             s.lat         = {lat},
             s.lng         = {lng},
             s.street      = {street},
             s.zip         = {zip},
             s.city        = {city},
             s.country     = {country},
             s.email       = {email},
             s.telephone   = {telephone},
             s.homepage    = {homepage},
             s.license     = {license}
         FOREACH (c IN cats |
           MERGE c-[:BELONGS_TO]->s
         )",
        {
            "id"          => &e.id,
            "title"       => &e.title,
            "description" => &e.description,
            "lat"         => &e.lat,
            "lng"         => &e.lng,
            "street"      => &e.street,
            "zip"         => &e.zip,
            "city"        => &e.city,
            "country"     => &e.country,
            "email"       => &e.email,
            "telephone"   => &e.telephone,
            "homepage"    => &e.homepage,
            "license"     => &e.license,
            "categories"  => &e.categories
        })?)?;
        Ok(())
    }

    fn create_tag(&mut self, t: &Tag) -> Result<()> {
        self.exec(cypher_stmt!("MERGE (t:Tag {id:{id}})",
        { "id" => &t.id })?)?;
        Ok(())
    }

    fn create_user(&mut self, u: &User) -> Result<()> {
        self.exec(cypher_stmt!(
        "MERGE (
           u:User {
             username:{username},
             password:{password},
             email:{email},
           }
        )",
        {
            "username" => &u.username,
            "password" => &u.password,
            "email"    => &u.email
        })?)?;
        Ok(())
    }

    fn create_rating(&mut self, r: &Rating) -> Result<()> {
        self.exec(cypher_stmt!(
        "MERGE (
           r:Rating {
             id      : {id},
             created : {created},
             value   : {value},
             context : {context},
           }
        )",
        {
            "id"        => &r.id,
            "created"   => &r.created,
            "value"     => &r.value,
            "context"   => &r.context
        })?)?;
        Ok(())
    }

    fn create_comment(&mut self, c: &Comment) -> Result<()> {
        self.exec(cypher_stmt!(
        "MERGE (
           c:Comment {
             id      : {id},
             created : {created},
             text    : {text},
           }
        )",
        {
            "id"        => &c.id,
            "created"   => &c.created,
            "value"     => &c.text
        })?)?;
        Ok(())
    }

    fn create_triple(&mut self, t: &Triple) -> Result<()> {
        let predicate = match t.predicate {
            Relation::IsTaggedWith => "IS_TAGGED_WITH",
            Relation::IsRatedWith => "IS_RATED_WITH",
            Relation::IsCommentedWith => "IS_COMMENTED_WITH",
            Relation::CreatedBy => "CREATED_BY"
        };
        let (subject_type, subject_id) = match t.subject {
            ObjectId::Entry(ref id) => ("Entry",id),
            ObjectId::Tag(ref id) => ("Tag",id),
            ObjectId::User(ref id) => ("User",id),
            ObjectId::Comment(ref id) => ("Comment",id),
            ObjectId::Rating(ref id) => ("Rating",id)
        };
        let (object_type, object_id) = match t.object {
            ObjectId::Entry(ref id) => ("Entry",id),
            ObjectId::Tag(ref id) => ("Tag",id),
            ObjectId::User(ref id) => ("User",id),
            ObjectId::Comment(ref id) => ("Comment",id),
            ObjectId::Rating(ref id) => ("Rating",id)
        };
        let stmt = format!(
           "MATCH (s:{s_type})
            WHERE s.id = \"{s_id}\"
            WITH s
            MATCH (o:{o_type})
            WHERE o.id = \"{o_id}\"
            WITH s,o
            MERGE (s)-[:{predicate}]->(o)",
                s_type = subject_type,
                s_id = subject_id,
                o_type = object_type,
                o_id = object_id,
                predicate = predicate
            );
        self.exec(stmt)?;
        Ok(())
    }

    fn update_entry(&mut self, e: &Entry) -> Result<()> {
        self.exec(cypher_stmt!(
        "MATCH (e:Entry)<--(s:EntryState) WHERE e.id = {id}
         WITH max(s.version) as v
         MATCH (e:Entry)<--(old:EntryState)
         WHERE e.id = {id} AND old.version = v AND old.version + 1 = {version}
         WITH e
         MATCH (c:Category)
         WHERE c.id in {categories}
         WITH e, collect(DISTINCT c) AS cats, count(c) AS cnt
         WHERE cnt > 0
         MERGE e<-[:BELONGS_TO]-(s:EntryState {
           created : timestamp(),
           version : {version}
         })
         SET s.title       = {title},
             s.description = {description},
             s.lat         = {lat},
             s.lng         = {lng},
             s.street      = {street},
             s.zip         = {zip},
             s.city        = {city},
             s.country     = {country},
             s.email       = {email},
             s.telephone   = {telephone},
             s.homepage    = {homepage},
             s.license     = {license}
         FOREACH (c IN cats |
           MERGE (c)-[:BELONGS_TO]->s
         )",
        {
            "id"          => &e.id,
            "version"     => &e.version,
            "title"       => &e.title,
            "description" => &e.description,
            "lat"         => &e.lat,
            "lng"         => &e.lng,
            "street"      => &e.street,
            "zip"         => &e.zip,
            "city"        => &e.city,
            "country"     => &e.country,
            "email"       => &e.email,
            "telephone"   => &e.telephone,
            "homepage"    => &e.homepage,
            "license"     => &e.license,
            "categories"  => &e.categories
        })?)?;
        Ok(())
    }

    fn all_categories(&self) -> Result<Vec<Category>> {
        let result = self.exec(
        "MATCH (c:Category)<--(s:CategoryState)
         RETURN {
           id      : c.id,
           version : s.version,
           created : s.created,
           name    : s.name
         } AS c")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Category>("c").ok())
            .collect::<Vec<Category>>())
    }

    fn all_triples(&self) -> Result<Vec<Triple>> {
        //TODO: extend for category
        let result = self.exec(
        "MATCH (e:Entry)-[IS_TAGGED_WITH]->(t:Tag)
         RETURN {
           subject   : { entry: e.id },
           predicate : \"is_tagged_with\",
           object    : { tag: t.id }
         } AS t")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Triple>("t").ok())
            .collect::<Vec<Triple>>())
    }

    fn all_tags(&self) -> Result<Vec<Tag>> {
        let result = self.exec(
        "MATCH (t:Tag) RETURN t")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Tag>("t").ok())
            .collect::<Vec<Tag>>())
    }

    fn all_ratings(&self) -> Result<Vec<Rating>> {
        let result = self.exec(
        "MATCH (r:Rating) RETURN r")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Rating>("r").ok())
            .collect::<Vec<Rating>>())
    }

    fn all_comments(&self) -> Result<Vec<Comment>> {
        let result = self.exec(
        "MATCH (c:Comment) RETURN c")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Comment>("c").ok())
            .collect::<Vec<Comment>>())
    }

    fn delete_triple(&mut self, t: &Triple) -> Result<()> {
        let predicate = match t.predicate {
            Relation::IsTaggedWith => "IS_TAGGED_WITH",
            Relation::IsRatedWith => "IS_RATED_WITH",
            Relation::IsCommentedWith => "IS_COMMENTED_WITH",
            Relation::CreatedBy => "CREATED_BY"
        };
        let (subject_type, subject_id) = match t.subject {
            ObjectId::Entry(ref id) => ("Entry",id),
            ObjectId::Tag(ref id) => ("Tag",id),
            ObjectId::User(ref id) => ("User",id),
            ObjectId::Comment(ref id) => ("Comment",id),
            ObjectId::Rating(ref id) => ("Rating",id)
        };
        let (object_type, object_id) = match t.object {
            ObjectId::Entry(ref id) => ("Entry",id),
            ObjectId::Tag(ref id) => ("Tag",id),
            ObjectId::User(ref id) => ("User",id),
            ObjectId::Comment(ref id) => ("Comment",id),
            ObjectId::Rating(ref id) => ("Rating",id)
        };
        let stmt = format!(
           "MATCH (s:{s_type})-[p:{predicate}]->(o:{o_type})
            WHERE s.id = \"{s_id}\" AND o.id = \"{o_id}\"
            DELETE p",
                s_type = subject_type,
                s_id = subject_id,
                o_type = object_type,
                o_id = object_id,
                predicate = predicate
            );
        self.exec(stmt)?;
        Ok(())
    }
}
<commit_msg>fix(neo4j): receive all triples<commit_after>use business::db::Db;
use entities::*;
use rusted_cypher::GraphClient;
use business::error::RepoError;
use std::result;

type Result<T> = result::Result<T, RepoError>;

fn neo4j_edge_name<'a>(t: &'a Triple) -> &'a str {
    match t.predicate {
       Relation::IsTaggedWith    => "IS_TAGGED_WITH",
       Relation::IsRatedWith     => "IS_RATED_WITH",
       Relation::IsCommentedWith => "IS_COMMENTED_WITH",
       Relation::CreatedBy       => "CREATED_BY"
    }.into()
}

fn object_id_to_neo4j_label<'a>(id: &'a ObjectId) -> (&'a str,&'a str) {
    match *id {
        ObjectId::Entry(ref id) => ("Entry",id),
        ObjectId::Tag(ref id) => ("Tag",id),
        ObjectId::User(ref id) => ("User",id),
        ObjectId::Comment(ref id) => ("Comment",id),
        ObjectId::Rating(ref id) => ("Rating",id)
    }
}

fn neo4j_label_to_object_id(label: &str, id: String) -> Option<ObjectId> {
    match label {
        "Entry"     => Some(ObjectId::Entry(id)),
        "Tag"       => Some(ObjectId::Tag(id)),
        "User"      => Some(ObjectId::User(id)),
        "Comment"   => Some(ObjectId::Comment(id)),
        "Rating"    => Some(ObjectId::Rating(id)),
        _           => None,
    }
}

fn neo4j_relation_to_relation(rel: &str) -> Option<Relation> {
    match rel {
        "IS_TAGGED_WITH"    => Some(Relation::IsTaggedWith),
        "IS_RATED_WITH"     => Some(Relation::IsRatedWith),
        "IS_COMMENTED_WITH" => Some(Relation::IsCommentedWith),
        "CREATED_BY"        => Some(Relation::CreatedBy),
        _                   => None
    }
}

#[derive(Deserialize,Debug)]
struct Neo4jObj {
    labels: Vec<String>,
    id: String
}

#[derive(Deserialize,Debug)]
struct Neo4jTriple {
    subject: Neo4jObj,
    predicate: String,
    object: Neo4jObj
}

fn from_neo_triple(t: Neo4jTriple) -> Option<Triple> {
    if t.subject.labels.is_empty() || t.object.labels.is_empty() {
        warn!("No labels found in {:?}",t);
        return None;
    }
    let subject = neo4j_label_to_object_id(&t.subject.labels[0], t.subject.id);
    let predicate = neo4j_relation_to_relation(&t.predicate);
    let object = neo4j_label_to_object_id(&t.object.labels[0], t.object.id);

    if let (Some(s), Some(p), Some(o)) = (subject, predicate, object) {
        Some(Triple {
            subject: s,
            predicate: p,
            object: o
        })
    } else {
        None
    }
}

impl Db for GraphClient {

    fn get_entry(&self, id: &str) -> Result<Entry> {
        let result = self.exec(cypher_stmt!(
        "MATCH (e:Entry)<--(s:EntryState) WHERE e.id = {id}
         WITH max(s.version) as version
         MATCH (e:Entry)<--(s:EntryState)
         WHERE e.id = {id} AND s.version = version
         WITH e, s
         MATCH s<-[:BELONGS_TO]-(c:Category)
         WITH e, s, collect(DISTINCT c.id) as categories
         RETURN {
           id          : e.id,
           created     : s.created,
           version     : s.version,
           title       : s.title,
           description : s.description,
           lat         : s.lat,
           lng         : s.lng,
           street      : s.street,
           zip         : s.zip,
           city        : s.city,
           country     : s.country,
           email       : s.email,
           telephone   : s.telephone,
           homepage    : s.homepage,
           categories  : categories,
           license     : s.license
         } AS e
         ORDER BY e.created DESC", {"id" => &id})?)?;
        let r = result.rows().next().ok_or(RepoError::NotFound)?;
        let e = r.get::<Entry>("e")?;
        Ok(e)
    }

    fn get_user(&self, username: &str) -> Result<User> {
        let result = self.exec(cypher_stmt!(
        "MATCH u:User
         WHERE u.username = {username}",
        { "username" => username })?)?;
        let r = result.rows().next().ok_or(RepoError::NotFound)?;
        let u = r.get::<User>("u")?;
        Ok(u)
    }

    fn all_entries(&self) -> Result<Vec<Entry>> {
        let result = self.exec(
        "MATCH (e:Entry)<--(x:EntryState)
          WITH distinct e, max(x.created) as max
          MATCH e<--(s:EntryState)
          WHERE s.created = max
          WITH e,s
          MATCH e<-[:BELONGS_TO]-s
          OPTIONAL MATCH (s)<-[:BELONGS_TO]-(c:Category)
          WITH e, s, collect(DISTINCT c.id) as categories
          RETURN {
            id          : e.id,
            created     : s.created,
            version     : s.version,
            title       : s.title,
            description : s.description,
            lat         : s.lat,
            lng         : s.lng,
            street      : s.street,
            zip         : s.zip,
            city        : s.city,
            country     : s.country,
            email       : s.email,
            telephone   : s.telephone,
            homepage    : s.homepage,
            categories  : categories,
            license     : s.license
          } AS e
          ORDER BY e.created DESC")?;
        Ok(result.rows()
            .filter_map(|r| r.get::<Entry>("e").ok())
            .collect::<Vec<Entry>>())
    }

    fn create_entry(&mut self, e: &Entry) -> Result<()> {
        self.exec(cypher_stmt!(
        "MATCH (c:Category)
         WHERE c.id in {categories}
         WITH
            collect(DISTINCT c)    AS cats,
            collect(DISTINCT c.id) AS cat_ids,
            count(c)               AS cnt
         WHERE cnt > 0
         CREATE (e:Entry {id:{id}})
         MERGE e<-[:BELONGS_TO]-(s:EntryState {
           created : timestamp(),
           version : 1
         })
         SET s.title       = {title},
             s.description = {description},
             s.lat         = {lat},
             s.lng         = {lng},
             s.street      = {street},
             s.zip         = {zip},
             s.city        = {city},
             s.country     = {country},
             s.email       = {email},
             s.telephone   = {telephone},
             s.homepage    = {homepage},
             s.license     = {license}
         FOREACH (c IN cats |
           MERGE c-[:BELONGS_TO]->s
         )",
        {
            "id"          => &e.id,
            "title"       => &e.title,
            "description" => &e.description,
            "lat"         => &e.lat,
            "lng"         => &e.lng,
            "street"      => &e.street,
            "zip"         => &e.zip,
            "city"        => &e.city,
            "country"     => &e.country,
            "email"       => &e.email,
            "telephone"   => &e.telephone,
            "homepage"    => &e.homepage,
            "license"     => &e.license,
            "categories"  => &e.categories
        })?)?;
        Ok(())
    }

    fn create_tag(&mut self, t: &Tag) -> Result<()> {
        self.exec(cypher_stmt!("MERGE (t:Tag {id:{id}})",
        { "id" => &t.id })?)?;
        Ok(())
    }

    fn create_user(&mut self, u: &User) -> Result<()> {
        self.exec(cypher_stmt!(
        "MERGE (
           u:User {
             username:{username},
             password:{password},
             email:{email},
           }
        )",
        {
            "username" => &u.username,
            "password" => &u.password,
            "email"    => &u.email
        })?)?;
        Ok(())
    }

    fn create_rating(&mut self, r: &Rating) -> Result<()> {
        self.exec(cypher_stmt!(
        "MERGE (r:Rating {
             id      : {id},
             created : {created},
             value   : {value},
             context : {context}
        })",
        {
            "id"        => &r.id,
            "created"   => &r.created,
            "value"     => &r.value,
            "context"   => &r.context
        })?)?;
        Ok(())
    }

    fn create_comment(&mut self, c: &Comment) -> Result<()> {
        self.exec(cypher_stmt!(
        "MERGE (
           c:Comment {
             id      : {id},
             created : {created},
             text    : {text}
           }
        )",
        {
            "id"        => &c.id,
            "created"   => &c.created,
            "text"      => &c.text
        })?)?;
        Ok(())
    }

    fn create_triple(&mut self, t: &Triple) -> Result<()> {

        let predicate = neo4j_edge_name(t);
        let (subject_type, subject_id) = object_id_to_neo4j_label(&t.subject);
        let (object_type, object_id) = object_id_to_neo4j_label(&t.object);

        let stmt = format!(
           "MATCH (s:{s_type})
            WHERE s.id = \"{s_id}\"
            WITH s
            MATCH (o:{o_type})
            WHERE o.id = \"{o_id}\"
            WITH s,o
            MERGE (s)-[:{predicate}]->(o)",
                s_type = subject_type,
                s_id = subject_id,
                o_type = object_type,
                o_id = object_id,
                predicate = predicate
            );
        self.exec(stmt)?;
        Ok(())
    }

    fn update_entry(&mut self, e: &Entry) -> Result<()> {
        self.exec(cypher_stmt!(
        "MATCH (e:Entry)<--(s:EntryState) WHERE e.id = {id}
         WITH max(s.version) as v
         MATCH (e:Entry)<--(old:EntryState)
         WHERE e.id = {id} AND old.version = v AND old.version + 1 = {version}
         WITH e
         MATCH (c:Category)
         WHERE c.id in {categories}
         WITH e, collect(DISTINCT c) AS cats, count(c) AS cnt
         WHERE cnt > 0
         MERGE e<-[:BELONGS_TO]-(s:EntryState {
           created : timestamp(),
           version : {version}
         })
         SET s.title       = {title},
             s.description = {description},
             s.lat         = {lat},
             s.lng         = {lng},
             s.street      = {street},
             s.zip         = {zip},
             s.city        = {city},
             s.country     = {country},
             s.email       = {email},
             s.telephone   = {telephone},
             s.homepage    = {homepage},
             s.license     = {license}
         FOREACH (c IN cats |
           MERGE (c)-[:BELONGS_TO]->s
         )",
        {
            "id"          => &e.id,
            "version"     => &e.version,
            "title"       => &e.title,
            "description" => &e.description,
            "lat"         => &e.lat,
            "lng"         => &e.lng,
            "street"      => &e.street,
            "zip"         => &e.zip,
            "city"        => &e.city,
            "country"     => &e.country,
            "email"       => &e.email,
            "telephone"   => &e.telephone,
            "homepage"    => &e.homepage,
            "license"     => &e.license,
            "categories"  => &e.categories
        })?)?;
        Ok(())
    }

    fn all_categories(&self) -> Result<Vec<Category>> {
        let result = self.exec(
        "MATCH (c:Category)<--(s:CategoryState)
         RETURN {
           id      : c.id,
           version : s.version,
           created : s.created,
           name    : s.name
         } AS c")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Category>("c").ok())
            .collect::<Vec<Category>>())
    }

    fn all_triples(&self) -> Result<Vec<Triple>> {
        let result = self.exec(
        "MATCH (s)-[p]->(o)
         WHERE (s.id IS NOT NULL) AND (o.id IS NOT NULL)
         RETURN {
            subject: { id: s.id, labels: labels(s) },
            predicate: type(p),
            object: { id: o.id, labels: labels(o) }
         } AS t")?;
        Ok(result
            .rows()
            .filter_map(|r|{
                match r.get::<Neo4jTriple>("t") {
                    Err(err) => {
                         warn!("{}",err);
                         None
                    }
                    Ok(t) => from_neo_triple(t)
                }
            })
            .collect::<Vec<Triple>>())
    }

    fn all_tags(&self) -> Result<Vec<Tag>> {
        let result = self.exec(
        "MATCH (t:Tag) RETURN t")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Tag>("t").ok())
            .collect::<Vec<Tag>>())
    }

    fn all_ratings(&self) -> Result<Vec<Rating>> {
        let result = self.exec(
        "MATCH (r:Rating) RETURN r")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Rating>("r").ok())
            .collect::<Vec<Rating>>())
    }

    fn all_comments(&self) -> Result<Vec<Comment>> {
        let result = self.exec(
        "MATCH (c:Comment) RETURN c")?;
        Ok(result
            .rows()
            .filter_map(|r| r.get::<Comment>("c").ok())
            .collect::<Vec<Comment>>())
    }

    fn delete_triple(&mut self, t: &Triple) -> Result<()> {
        let predicate = neo4j_edge_name(t);
        let (subject_type, subject_id) = object_id_to_neo4j_label(&t.subject);
        let (object_type, object_id) = object_id_to_neo4j_label(&t.object);
        let stmt = format!(
           "MATCH (s:{s_type})-[p:{predicate}]->(o:{o_type})
            WHERE s.id = \"{s_id}\" AND o.id = \"{o_id}\"
            DELETE p",
                s_type = subject_type,
                s_id = subject_id,
                o_type = object_type,
                o_id = object_id,
                predicate = predicate
            );
        self.exec(stmt)?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use super::libc::{c_int, close};
use std::fmt;

pub struct CFileDescriptor {
    fd: c_int
}

impl CFileDescriptor {
    pub fn new(fd: c_int) -> CFileDescriptor {
        CFileDescriptor {fd: fd}
    }
}

impl Drop for CFileDescriptor {
    fn drop(&mut self) {
        let result = unsafe {
            close(self.fd)
        };

        // There isn't much we can do if this failed
        if result != 0 {
            println!("Warning: failed to close file descriptor when dropping");
        }
    }
}

impl fmt::Show for CFileDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<C File Descriptor {}>", self.fd)
    }
}
<commit_msg>Implement Reader trait on c_file_descriptor<commit_after>use super::libc::{c_int, close, read, c_void, size_t};
use std::fmt;
use std::io::{Reader, IoResult, IoError, standard_error, EndOfFile};

pub struct CFileDescriptor {
    fd: c_int
}

impl CFileDescriptor {
    pub fn new(fd: c_int) -> CFileDescriptor {
        CFileDescriptor {fd: fd}
    }
}

impl Reader for CFileDescriptor {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        let buf_len = buf.len() as i64;
        let buf_ptr = buf.as_ptr();

        unsafe {
            let bytes_read =  read(self.fd, buf_ptr as *mut c_void, buf_len as size_t);

            match bytes_read {
                0 => Err(standard_error(EndOfFile)),

                -1 => Err(IoError::last_error()),

                n @ _  => {
                    if n > buf_len {
                        // If C has screwed us by reading more than the buffer length, then we're in a bad spot. Our
                        // return address might have been overwritten on the stack, as buf would have been in our
                        // caller's stack frame.
                        fail!("Buffer tainted by C read. Unsafe to continue")
                    }

                    Ok(n as uint)
                }
            }
        }
    }
}

impl Drop for CFileDescriptor {
    fn drop(&mut self) {
        let result = unsafe {
            close(self.fd)
        };

        // There isn't much we can do if this failed
        if result != 0 {
            println!("Warning: failed to close file descriptor when dropping");
        }
    }
}

impl fmt::Show for CFileDescriptor {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<C File Descriptor {}>", self.fd)
    }
}
<|endoftext|>


<commit_before>#![feature(result_expect)]
#![feature(drain)]
extern crate mio;
extern crate nix;
extern crate coroutine;
extern crate mioco;

use mio::*;
use mio::tcp::*;
use mio::util::Slab;
use std::io;
use std::collections::HashSet;

use std::net::SocketAddr;
use std::str::FromStr;

const SERVER: Token = Token(0);
const CONN_TOKEN_START : Token = Token(1);
const CONNS_MAX : usize = 256;
const DEFAULT_LISTEN_ADDR : &'static str = "127.0.0.1:5555";

fn listend_addr() -> SocketAddr {
    FromStr::from_str(DEFAULT_LISTEN_ADDR).unwrap()
}

struct Server {
    sock: TcpListener,
    conns: Slab<mioco::ExternalHandle>,
    to_del: HashSet<Token>,
}

impl Server {
    fn new(addr : SocketAddr) -> io::Result<(Server, EventLoop<Server>)> {

        let sock = try!(TcpSocket::v4());

        try!(sock.set_reuseaddr(true));
        try!(sock.bind(&addr));

        let sock = try!(sock.listen(1024));
        let config = EventLoopConfig {
            io_poll_timeout_ms: 1,
            notify_capacity: 4_096,
            messages_per_tick: 256,
            timer_tick_ms: 1,
            timer_wheel_size: 1_024,
            timer_capacity: 65_536,
        };
        let mut ev_loop : EventLoop<Server> = try!(EventLoop::configured(config));

        try!(ev_loop.register_opt(&sock, SERVER, EventSet::readable(), PollOpt::edge()));

        Ok((Server {
            sock: sock,
            conns: Slab::new_starting_at(CONN_TOKEN_START, CONNS_MAX),
            to_del: HashSet::new(),
        }, ev_loop))
    }

    fn accept(&mut self, event_loop: &mut EventLoop<Server>) -> io::Result<()> {

        use std::os::unix::io::AsRawFd;
        use nix::sys::socket;
        loop {
            let sock = match try!(self.sock.accept()) {
                None => break,
                Some(sock) => sock,
            };

            // Don't buffer output in TCP - kills latency sensitive benchmarks
            try!(socket::setsockopt(
                    sock.as_raw_fd(), socket::SockLevel::Tcp, socket::sockopt::TcpNoDelay, &true
                    ).map_err(|e| io::Error::from_raw_os_error(e.errno() as i32)));

            for token in self.to_del.drain() {
                self.conns.remove(token);
            }

            let _tok = self.conns.insert_with(|token| {
                let mut builder = mioco::Builder::new();
                let io : mioco::ExternalHandle = builder.wrap_io(event_loop, sock, token);

                let f = move |io : &mut mioco::CoroutineHandle| {
                    use std::io::{Read, Write};
                    let mut io = &mut io.handles()[0];

                    let mut buf = [0u8; 1024 * 16];
                    loop {

                        let res = io.read(&mut buf);

                        match res {
                            Err(_) => return,
                            Ok(0) => /* EOF */ return,
                            Ok(size) => {
                                let mut write_i = 0;
                                loop {
                                    if write_i == size {
                                        break;
                                    }

                                    match io.write(&mut buf[write_i..size]) {
                                        Ok(written) => write_i += written,
                                        Err(_) => return,
                                    }
                                }
                            }
                        }
                    }
                };

                builder.start(f, event_loop);

                io
            });
        }

        Ok(())
    }

    fn conn_handle_finished(&mut self, event_loop : &mut EventLoop<Server>, token : Token, finished : bool) {
        if finished {
            if !self.to_del.contains(&token) {
                let handle = self.conns[token].clone();

                handle.for_every_token(|token| {
                    let mut handle = &mut self.conns[token];
                    handle.deregister(event_loop);
                    self.to_del.insert(token);
                });
            }
        }
    }

    fn conn_ready(&mut self, event_loop : &mut EventLoop<Server>, tok: Token, events : EventSet) {
        let finished = {
            let conn = self.conn(tok);
            conn.ready(event_loop, tok, events);
            conn.is_finished()
        };
        self.conn_handle_finished(event_loop, tok, finished);
    }

    fn conn<'a>(&'a mut self, tok : Token) -> &'a mut mioco::ExternalHandle {
        &mut self.conns[tok]
    }
}

impl Handler for Server {
    type Timeout = usize;
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<Server>, token: Token, events: EventSet) {
        match token {
            SERVER => self.accept(event_loop).expect("accept(event_loop) failed"),
            i => self.conn_ready(event_loop, i, events),
        };
    }
}

pub fn main() {
    let addr = listend_addr();

    let (mut server, mut ev_loop) = Server::new(addr).expect("Server::new(...) failed");

    // Start the event loop
    println!("Starting tcp echo server on {:?}", server.sock.local_addr().unwrap());
    ev_loop.run(&mut server).unwrap();
}
<commit_msg>Remove workaround fixed by mio event coalescing.<commit_after>#![feature(result_expect)]
extern crate mio;
extern crate nix;
extern crate coroutine;
extern crate mioco;

use mio::*;
use mio::tcp::*;
use mio::util::Slab;
use std::io;

use std::net::SocketAddr;
use std::str::FromStr;

const SERVER: Token = Token(0);
const CONN_TOKEN_START : Token = Token(1);
const CONNS_MAX : usize = 256;
const DEFAULT_LISTEN_ADDR : &'static str = "127.0.0.1:5555";

fn listend_addr() -> SocketAddr {
    FromStr::from_str(DEFAULT_LISTEN_ADDR).unwrap()
}

struct Server {
    sock: TcpListener,
    conns: Slab<mioco::ExternalHandle>,
}

impl Server {
    fn new(addr : SocketAddr) -> io::Result<(Server, EventLoop<Server>)> {

        let sock = try!(TcpSocket::v4());

        try!(sock.set_reuseaddr(true));
        try!(sock.bind(&addr));

        let sock = try!(sock.listen(1024));
        let config = EventLoopConfig {
            io_poll_timeout_ms: 1,
            notify_capacity: 4_096,
            messages_per_tick: 256,
            timer_tick_ms: 1,
            timer_wheel_size: 1_024,
            timer_capacity: 65_536,
        };
        let mut ev_loop : EventLoop<Server> = try!(EventLoop::configured(config));

        try!(ev_loop.register_opt(&sock, SERVER, EventSet::readable(), PollOpt::edge()));

        Ok((Server {
            sock: sock,
            conns: Slab::new_starting_at(CONN_TOKEN_START, CONNS_MAX),
        }, ev_loop))
    }

    fn accept(&mut self, event_loop: &mut EventLoop<Server>) -> io::Result<()> {

        use std::os::unix::io::AsRawFd;
        use nix::sys::socket;
        loop {
            let sock = match try!(self.sock.accept()) {
                None => break,
                Some(sock) => sock,
            };

            // Don't buffer output in TCP - kills latency sensitive benchmarks
            try!(socket::setsockopt(
                    sock.as_raw_fd(), socket::SockLevel::Tcp, socket::sockopt::TcpNoDelay, &true
                    ).map_err(|e| io::Error::from_raw_os_error(e.errno() as i32)));

            let _tok = self.conns.insert_with(|token| {
                let mut builder = mioco::Builder::new();
                let io : mioco::ExternalHandle = builder.wrap_io(event_loop, sock, token);

                let f = move |io : &mut mioco::CoroutineHandle| {
                    use std::io::{Read, Write};
                    let mut io = &mut io.handles()[0];

                    let mut buf = [0u8; 1024 * 16];
                    loop {

                        let res = io.read(&mut buf);

                        match res {
                            Err(_) => return,
                            Ok(0) => /* EOF */ return,
                            Ok(size) => {
                                let mut write_i = 0;
                                loop {
                                    if write_i == size {
                                        break;
                                    }

                                    match io.write(&mut buf[write_i..size]) {
                                        Ok(written) => write_i += written,
                                        Err(_) => return,
                                    }
                                }
                            }
                        }
                    }
                };

                builder.start(f, event_loop);

                io
            });
        }

        Ok(())
    }

    fn conn_handle_finished(&mut self, token : Token, finished : bool) {
        if finished {
            self.conns.remove(token);
        }
    }

    fn conn_ready(&mut self, event_loop : &mut EventLoop<Server>, tok: Token, events : EventSet) {
        let finished = {
            let conn = self.conn(tok);
            conn.ready(event_loop, tok, events);
            conn.is_finished()
        };
        self.conn_handle_finished(tok, finished);
    }

    fn conn<'a>(&'a mut self, tok : Token) -> &'a mut mioco::ExternalHandle {
        &mut self.conns[tok]
    }
}

impl Handler for Server {
    type Timeout = usize;
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop<Server>, token: Token, events: EventSet) {
        match token {
            SERVER => self.accept(event_loop).expect("accept(event_loop) failed"),
            i => self.conn_ready(event_loop, i, events),
        };
    }
}

pub fn main() {
    let addr = listend_addr();

    let (mut server, mut ev_loop) = Server::new(addr).expect("Server::new(...) failed");

    // Start the event loop
    println!("Starting tcp echo server on {:?}", server.sock.local_addr().unwrap());
    ev_loop.run(&mut server).unwrap();
}
<|endoftext|>


<commit_before>extern crate core;

use core::num::FromPrimitive;
use core::fmt;

use std::rand::random;

use Direction::{SpinUp, SpinDown, SpinSuper};
use Plan::{Trivial, OddBall};

#[derive(Copy)]
#[derive(Show)]
#[derive(PartialEq)]
enum Direction {
    SpinUp,
    SpinDown,
    SpinSuper,
}

impl fmt::String for Direction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let r = match self {
            &SpinUp => "SpinUp",
            &SpinDown => "SpinDown",
            &SpinSuper => "SpinSuper"
        };
        write!(f, "{}", r)
    }
}

impl fmt::String for Detector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Detector::{D12, D3, D9};
        let r = match self {
            &D12 => "12 oclock",
            &D3 => "3 oclock",
            &D9 => "9 oclock"
        };
        write!(f, "{}", r)
    }
}

struct Pair<T> {
    lhs: T,
    rhs: T
}

struct Particle {
    spin: Direction
}

enum Plan {
    Trivial, // up-up-up -> down-down-down
    OddBall  // up-down-up -> down-up-down
}

#[derive(Show)]
#[derive(PartialEq)]
enum Detector {
    D12, // 12 o'clock
    D3, // 3 o'clock
    D9, // 9 o' clock
}

impl Particle {
    fn new_pair () -> Pair<Particle> {
        let d1 = SpinSuper;
        let d2 = SpinSuper;
        let p1 = Particle{spin: d1};
        let p2 = Particle{spin: d2};

        return Pair{lhs: p1, rhs: p2};
    }

    pub fn measure (&mut self, detector: &Detector) {
        self.spin = match detector {
            &Detector::D12 => SpinUp,
            &Detector::D3 => match random::<f32>() {
                0.0  ... 0.25 => SpinDown,
                0.25 ... 1.00 => SpinUp,
                _ => unreachable!()
            },
            &Detector::D9 => match random::<f32>() {
                0.0  ... 0.25 => SpinDown,
                0.25 ... 1.00 => SpinUp,
                _ => unreachable!()
            },
        };
    }

    // NOTE on spooky and premeditated only if measured in the same
    // direction must the spins be opposite

    // measure with with a message
    pub fn spooky (&mut self, friend: &mut Particle) -> Pair<Direction> {

        let detector1 = Detector::D12;
        let detector2 = Particle::get_detector_direction();

        self.measure (&detector1);

        if detector1 == detector2 { // 1/3
            friend.spin = match self.spin {
                SpinUp => SpinDown,
                SpinDown => SpinUp,
                _ => unreachable!()
            };
        } else { // 2/3
            friend.measure(&detector2);
        }

        return Pair{lhs: self.spin, rhs: friend.spin};
    }

    fn get_detector_direction () -> Detector {
        let rnd = random::<f32>();

        let detector = match rnd {
            0.0       ... 0.3333333 => Detector::D12,
            0.3333333 ... 0.6666666 => Detector::D3,
            0.6666666 ... 1.00 => Detector::D9,
            _ => unreachable!()
        };

        return detector;
    }

    pub fn hidden_information (&mut self, friend: &mut Particle, plan: Plan) -> Pair<Direction> {

        let rnd = random::<f32>();

        let detector = Particle::get_detector_direction();
        let spin = match plan {
            Trivial => SpinUp,
            OddBall => match detector {
                Detector::D3 => SpinDown,
                _ => SpinUp
            }
        };
        friend.spin = spin;

        let detector = Particle::get_detector_direction();
        let spin = match plan {
            Trivial => SpinDown,
            OddBall => match detector {
                Detector::D3 => SpinUp,
                _ => SpinDown
            }
        };
        self.spin = spin;

        return Pair{lhs: self.spin, rhs: friend.spin};
    }
}

/**
 * run spooky for trials number of trials
 */
fn run_spooky (trials: f64) {
    let mut num_different: f64 = 0f64;

    let particles = Particle::new_pair();
    let mut lhs = particles.lhs;
    let mut rhs = particles.rhs;

    for _ in range(0, trials as usize) {
        let particles = Particle::new_pair();
        lhs = particles.lhs;
        rhs = particles.rhs;

        lhs.spooky(&mut rhs);

        if lhs.spin != rhs.spin {
            num_different += 1.0;
        }
    }

    println!("Percent different for spooky {}%", 100f64 * (num_different) / trials as f64);
    println!("      Should be about 1/2 or 50%");
}

/**
 * Run test for hidden information, set the plan_probability to choose
 * OddBall vs Trivial
 */
fn run_hidden(trials: f64, plan_probability: f32) {

    let mut num_different = 0f64;
    let particles = Particle::new_pair();
    let mut lhs = particles.lhs;
    let mut rhs = particles.rhs;

    for _ in range(0, trials as usize) {
        let particles = Particle::new_pair();
        lhs = particles.lhs;
        rhs = particles.rhs;

        if random::<f32>() < plan_probability {
            lhs.hidden_information(&mut rhs, OddBall);
        } else {
            lhs.hidden_information(&mut rhs, Trivial);
        }

        //println!("lhs.spin {}, rhs.spin {}", lhs.spin, rhs.spin);
        if lhs.spin != rhs.spin {
            num_different += 1.0;
        }
    }

    println!("Percent different for hidden info {}%", 100f64 * (num_different) / trials as f64);
    println!("  Should be greater than 5/9th or {}%", 100.0 * 5.0/9.0);
    println!("With OddBall choosen {}% of the time and Trivial {}% of the time",
             100.0 * plan_probability,
             100.0 * (1.0 - plan_probability));

}

// hidden_information should give +55.6% difference, spooky would give 50%
fn main () {

    let mut trials: f64 = 1000000f64;

    run_spooky(trials);
    run_hidden(trials, 0.5);
    run_hidden(trials, 1.0);
    run_hidden(trials, 0.0);
}
<commit_msg>cleanup for rustc 1.0.0-nightly (29bd9a06e 2015-01-20 23:03:09 +0000)<commit_after>extern crate core;

use core::fmt;

use std::rand::random;

use Direction::{SpinUp, SpinDown, SpinSuper};
use Plan::{Trivial, OddBall};

#[derive(Copy)]
#[derive(Show)]
#[derive(PartialEq)]
enum Direction {
    SpinUp,
    SpinDown,
    SpinSuper,
}

impl fmt::String for Direction {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let r = match self {
            &SpinUp => "SpinUp",
            &SpinDown => "SpinDown",
            &SpinSuper => "SpinSuper"
        };
        write!(f, "{}", r)
    }
}

impl fmt::String for Detector {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Detector::{D12, D3, D9};
        let r = match self {
            &D12 => "12 oclock",
            &D3 => "3 oclock",
            &D9 => "9 oclock"
        };
        write!(f, "{}", r)
    }
}

struct Pair<T> {
    lhs: T,
    rhs: T
}

struct Particle {
    spin: Direction
}

enum Plan {
    Trivial, // up-up-up -> down-down-down
    OddBall  // up-down-up -> down-up-down
}

#[derive(Show)]
#[derive(PartialEq)]
enum Detector {
    D12, // 12 o'clock
    D3, // 3 o'clock
    D9, // 9 o' clock
}

impl Particle {
    fn new_pair () -> Pair<Particle> {
        let d1 = SpinSuper;
        let d2 = SpinSuper;
        let p1 = Particle{spin: d1};
        let p2 = Particle{spin: d2};

        return Pair{lhs: p1, rhs: p2};
    }

    pub fn measure (&mut self, detector: &Detector) {
        self.spin = match detector {
            &Detector::D12 => SpinUp,
            &Detector::D3 => match rand32() {
                0.0  ... 0.25 => SpinDown,
                0.25 ... 1.00 => SpinUp,
                _ => unreachable!()
            },
            &Detector::D9 => match rand32() {
                0.0  ... 0.25 => SpinDown,
                0.25 ... 1.00 => SpinUp,
                _ => unreachable!()
            },
        };
    }

    // NOTE on spooky and premeditated only if measured in the same
    // direction must the spins be opposite

    // measure with with a message
    pub fn spooky (&mut self, friend: &mut Particle) -> Pair<Direction> {

        let detector1 = Detector::D12;
        let detector2 = Particle::get_detector_direction();

        self.measure (&detector1);

        if detector1 == detector2 { // 1/3
            friend.spin = match self.spin {
                SpinUp => SpinDown,
                SpinDown => SpinUp,
                _ => unreachable!()
            };
        } else { // 2/3
            friend.measure(&detector2);
        }

        return Pair{lhs: self.spin, rhs: friend.spin};
    }

    fn get_detector_direction () -> Detector {

        let detector = match rand32() {
            0.0       ... 0.3333333 => Detector::D12,
            0.3333333 ... 0.6666666 => Detector::D3,
            0.6666666 ... 1.00 => Detector::D9,
            _ => unreachable!()
        };

        return detector;
    }

    pub fn hidden_information (&mut self, friend: &mut Particle, plan: Plan) -> Pair<Direction> {

        let detector = Particle::get_detector_direction();
        let spin = match plan {
            Trivial => SpinUp,
            OddBall => match detector {
                Detector::D3 => SpinDown,
                _ => SpinUp
            }
        };
        friend.spin = spin;

        let detector = Particle::get_detector_direction();
        let spin = match plan {
            Trivial => SpinDown,
            OddBall => match detector {
                Detector::D3 => SpinUp,
                _ => SpinDown
            }
        };
        self.spin = spin;

        return Pair{lhs: self.spin, rhs: friend.spin};
    }
}

fn rand32 () -> f32 {
    return random::<f32>();
}

/**
 * run spooky for trials number of trials
 */
fn run_spooky (trials: f64) {
    let mut num_different: f64 = 0f64;

    let mut lhs;
    let mut rhs;

    for _ in 0 .. trials as usize {
        let particles = Particle::new_pair();
        lhs = particles.lhs;
        rhs = particles.rhs;

        lhs.spooky(&mut rhs);

        if lhs.spin != rhs.spin {
            num_different += 1.0;
        }
    }

    println!("Percent different for spooky {}%", 100f64 * (num_different) / trials as f64);
    println!("      Should be about 1/2 or 50%");
}

/**
 * Run test for hidden information, set the plan_probability to choose
 * OddBall vs Trivial
 */
fn run_hidden(trials: f64, plan_probability: f32) {

    let mut num_different = 0f64;
    let mut lhs;
    let mut rhs;

    for _ in 0 .. trials as usize {
        let particles = Particle::new_pair();
        lhs = particles.lhs;
        rhs = particles.rhs;

        if rand32() < plan_probability {
            lhs.hidden_information(&mut rhs, OddBall);
        } else {
            lhs.hidden_information(&mut rhs, Trivial);
        }

        //println!("lhs.spin {}, rhs.spin {}", lhs.spin, rhs.spin);
        if lhs.spin != rhs.spin {
            num_different += 1.0;
        }
    }

    println!("Percent different for hidden info {}%", 100f64 * (num_different) / trials as f64);
    println!("  Should be greater than 5/9th or {}%", 100.0 * 5.0/9.0);
    println!("With OddBall choosen {}% of the time and Trivial {}% of the time",
             100.0 * plan_probability,
             100.0 * (1.0 - plan_probability));

}

// hidden_information should give +55.6% difference, spooky would give 50%
fn main () {

    let trials: f64 = 1000000f64;

    run_spooky(trials);
    run_hidden(trials, 0.5);
    run_hidden(trials, 1.0);
    run_hidden(trials, 0.0);
}
<|endoftext|>


<commit_before>use std::kinds::marker::{ContravariantLifetime, NoCopy};

use objc::runtime::Object;
use objc::{Id, IdVector, IntoIdVector, Owned, Ownership, Shared, ShareId};

use {class, INSCopying, INSMutableCopying, INSObject};

pub struct NSRange {
    pub location: uint,
    pub length: uint,
}

pub struct NSEnumerator<'a, T> {
    id: Id<Object>,
    marker: ContravariantLifetime<'a>,
}

impl<'a, T> NSEnumerator<'a, T> {
    pub unsafe fn from_ptr(ptr: *mut Object) -> NSEnumerator<'a, T> {
        NSEnumerator { id: Id::from_ptr(ptr), marker: ContravariantLifetime }
    }
}

impl<'a, T> Iterator<&'a T> for NSEnumerator<'a, T> {
    fn next(&mut self) -> Option<&'a T> {
        unsafe {
            let obj = msg_send![self.id nextObject] as *mut T;
            obj.as_ref()
        }
    }
}

pub trait INSArray<T: INSObject, O: Ownership> : INSObject {
    fn count(&self) -> uint {
        let result = unsafe {
            msg_send![self count]
        };
        result as uint
    }

    fn object_at(&self, index: uint) -> &T {
        unsafe {
            let obj = msg_send![self objectAtIndex:index] as *const T;
            &*obj
        }
    }

    fn first_object(&self) -> Option<&T> {
        unsafe {
            let obj = msg_send![self firstObject] as *const T;
            obj.as_ref()
        }
    }

    fn last_object(&self) -> Option<&T> {
        unsafe {
            let obj = msg_send![self lastObject] as *const T;
            obj.as_ref()
        }
    }

    fn object_enumerator(&self) -> NSEnumerator<T> {
        unsafe {
            let result = msg_send![self objectEnumerator];
            NSEnumerator::from_ptr(result)
        }
    }

    unsafe fn from_refs(refs: &[&T]) -> Id<Self> {
        let cls = class::<Self>();
        let obj = msg_send![cls alloc];
        let obj = msg_send![obj initWithObjects:refs.as_ptr() count:refs.len()];
        Id::from_retained_ptr(obj as *mut Self)
    }

    fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self> {
        let refs = vec.as_refs_slice();
        unsafe {
            INSArray::from_refs(refs)
        }
    }

    fn objects_in_range(&self, start: uint, len: uint) -> Vec<&T> {
        let vec: Vec<*mut T> = Vec::from_elem(len, RawPtr::null());
        let range = NSRange { location: start, length: len };
        unsafe {
            msg_send![self getObjects:vec.as_ptr() range:range];
            vec.map_in_place(|ptr| &*ptr)
        }
    }

    fn to_vec(&self) -> Vec<&T> {
        self.objects_in_range(0, self.count())
    }

    fn into_vec(array: Id<Self>) -> Vec<Id<T, O>> {
        let vec = array.to_vec();
        unsafe {
            vec.into_id_vec()
        }
    }
}

pub trait INSOwnedArray<T: INSObject> : INSArray<T, Owned> {
    fn mut_object_at(&mut self, index: uint) -> &mut T {
        unsafe {
            let result = msg_send![self objectAtIndex:index] as *mut T;
            &mut *result
        }
    }
}

pub trait INSSharedArray<T: INSObject> : INSArray<T, Shared> {
    fn shared_object_at(&self, index: uint) -> ShareId<T> {
        let obj = self.object_at(index);
        unsafe {
            Id::from_ptr(obj as *const _ as *mut T)
        }
    }

    fn from_slice(slice: &[ShareId<T>]) -> Id<Self> {
        let refs = slice.as_refs_slice();
        unsafe {
            INSArray::from_refs(refs)
        }
    }

    fn to_shared_vec(&self) -> Vec<ShareId<T>> {
        let vec = self.to_vec();
        unsafe {
            vec.into_id_vec()
        }
    }
}

pub struct NSArray<T, O = Owned> {
    nocopy: NoCopy,
}

object_impl!(NSArray<T, O>)

impl<T: INSObject, O: Ownership> INSArray<T, O> for NSArray<T, O> { }

impl<T: INSObject> INSOwnedArray<T> for NSArray<T, Owned> { }

impl<T: INSObject> INSSharedArray<T> for NSArray<T, Shared> { }

impl<T> INSCopying<NSSharedArray<T>> for NSArray<T, Shared> { }

impl<T> INSMutableCopying<NSMutableSharedArray<T>> for NSArray<T, Shared> { }

impl<T: INSObject, O: Ownership> Index<uint, T> for NSArray<T, O> {
    fn index(&self, index: &uint) -> &T {
        self.object_at(*index)
    }
}

pub type NSSharedArray<T> = NSArray<T, Shared>;

pub trait INSMutableArray<T: INSObject, O: Ownership> : INSArray<T, O> {
    fn add_object(&mut self, obj: Id<T, O>) {
        unsafe {
            msg_send![self addObject:obj];
        }
    }

    fn insert_object_at(&mut self, index: uint, obj: Id<T, O>) {
        unsafe {
            msg_send![self insertObject:obj atIndex:index];
        }
    }

    fn replace_object_at(&mut self, index: uint, obj: Id<T, O>) -> Id<T, O> {
        let old_obj = unsafe {
            let obj = self.object_at(index);
            Id::from_ptr(obj as *const _ as *mut T)
        };
        unsafe {
            msg_send![self replaceObjectAtIndex:index withObject:obj];
        }
        old_obj
    }

    fn remove_object_at(&mut self, index: uint) -> Id<T, O> {
        let obj = unsafe {
            let obj = self.object_at(index);
            Id::from_ptr(obj as *const _ as *mut T)
        };
        unsafe {
            msg_send![self removeObjectAtIndex:index];
        }
        obj
    }

    fn remove_last_object(&mut self) -> Id<T, O> {
        let obj = self.last_object().map(|obj| unsafe {
            Id::from_ptr(obj as *const _ as *mut T)
        });
        unsafe {
            msg_send![self removeLastObject];
        }
        // removeLastObject would have failed if the array is empty,
        // so we know this won't be None
        obj.unwrap()
    }

    fn remove_all_objects(&mut self) {
        unsafe {
            msg_send![self removeAllObjects];
        }
    }
}

pub struct NSMutableArray<T, O = Owned> {
    nocopy: NoCopy,
}

object_impl!(NSMutableArray<T, O>)

impl<T: INSObject, O: Ownership> INSArray<T, O> for NSMutableArray<T, O> { }

impl<T: INSObject> INSOwnedArray<T> for NSMutableArray<T, Owned> { }

impl<T: INSObject> INSSharedArray<T> for NSMutableArray<T, Shared> { }

impl<T: INSObject, O: Ownership> INSMutableArray<T, O> for NSMutableArray<T, O> { }

impl<T> INSCopying<NSSharedArray<T>> for NSMutableArray<T, Shared> { }

impl<T> INSMutableCopying<NSMutableSharedArray<T>> for NSMutableArray<T, Shared> { }

impl<T: INSObject, O: Ownership> Index<uint, T> for NSMutableArray<T, O> {
    fn index(&self, index: &uint) -> &T {
        self.object_at(*index)
    }
}

pub type NSMutableSharedArray<T> = NSMutableArray<T, Shared>;

#[cfg(test)]
mod tests {
    use objc::{Id};
    use {INSObject, NSObject};
    use super::{INSArray, INSMutableArray, NSArray, NSMutableArray};

    fn sample_array(len: uint) -> Id<NSArray<NSObject>> {
        let vec: Vec<Id<NSObject>> = Vec::from_fn(len, |_| INSObject::new());
        INSArray::from_vec(vec)
    }

    #[test]
    fn test_count() {
        let empty_array: Id<NSArray<NSObject>> = INSObject::new();
        assert!(empty_array.count() == 0);

        let array = sample_array(4);
        assert!(array.count() == 4);
    }

    #[test]
    fn test_object_at() {
        let array = sample_array(4);
        assert!(array.object_at(0) != array.object_at(3));
        assert!(array.first_object().unwrap() == array.object_at(0));
        assert!(array.last_object().unwrap() == array.object_at(3));

        let empty_array: Id<NSArray<NSObject>> = INSObject::new();
        assert!(empty_array.first_object().is_none());
        assert!(empty_array.last_object().is_none());
    }

    #[test]
    fn test_object_enumerator() {
        let array = sample_array(4);

        assert!(array.object_enumerator().count() == 4);
        assert!(array.object_enumerator()
                     .enumerate()
                     .all(|(i, obj)| obj == array.object_at(i)));
    }

    #[test]
    fn test_objects_in_range() {
        let array = sample_array(4);

        let middle_objs = array.objects_in_range(1, 2);
        assert!(middle_objs.len() == 2);
        assert!(middle_objs[0] == array.object_at(1));
        assert!(middle_objs[1] == array.object_at(2));

        let empty_objs = array.objects_in_range(1, 0);
        assert!(empty_objs.len() == 0);

        let all_objs = array.objects_in_range(0, 4);
        assert!(all_objs.len() == 4);
    }

    #[test]
    fn test_into_vec() {
        let array = sample_array(4);

        let vec = INSArray::into_vec(array);
        assert!(vec.len() == 4);
    }

    #[test]
    fn test_add_object() {
        let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
        let obj: Id<NSObject> = INSObject::new();
        array.add_object(obj);

        assert!(array.count() == 1);
        assert!(array.object_at(0) == array.object_at(0));

        let obj: Id<NSObject> = INSObject::new();
        array.insert_object_at(0, obj);
        assert!(array.count() == 2);
    }

    #[test]
    fn test_replace_object() {
        let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
        let obj: Id<NSObject> = INSObject::new();
        array.add_object(obj);

        let obj: Id<NSObject> = INSObject::new();
        let old_obj = array.replace_object_at(0, obj);
        assert!(&*old_obj != array.object_at(0));
    }

    #[test]
    fn test_remove_object() {
        let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
        for _ in range(0u, 4) {
            let obj: Id<NSObject> = INSObject::new();
            array.add_object(obj);
        }

        array.remove_object_at(1);
        assert!(array.count() == 3);

        array.remove_last_object();
        assert!(array.count() == 2);

        array.remove_all_objects();
        assert!(array.count() == 0);
    }
}
<commit_msg>Added mutable array sort_with_context method.<commit_after>use std::kinds::marker::{ContravariantLifetime, NoCopy};

use objc::runtime::Object;
use objc::{Id, IdVector, IntoIdVector, Owned, Ownership, Shared, ShareId};

use {class, INSCopying, INSMutableCopying, INSObject};

#[repr(C)]
pub enum NSComparisonResult {
    Ascending  = -1i,
    Same       = 0i,
    Descending = 1i,
}

pub struct NSRange {
    pub location: uint,
    pub length: uint,
}

pub struct NSEnumerator<'a, T> {
    id: Id<Object>,
    marker: ContravariantLifetime<'a>,
}

impl<'a, T> NSEnumerator<'a, T> {
    pub unsafe fn from_ptr(ptr: *mut Object) -> NSEnumerator<'a, T> {
        NSEnumerator { id: Id::from_ptr(ptr), marker: ContravariantLifetime }
    }
}

impl<'a, T> Iterator<&'a T> for NSEnumerator<'a, T> {
    fn next(&mut self) -> Option<&'a T> {
        unsafe {
            let obj = msg_send![self.id nextObject] as *mut T;
            obj.as_ref()
        }
    }
}

pub trait INSArray<T: INSObject, O: Ownership> : INSObject {
    fn count(&self) -> uint {
        let result = unsafe {
            msg_send![self count]
        };
        result as uint
    }

    fn object_at(&self, index: uint) -> &T {
        unsafe {
            let obj = msg_send![self objectAtIndex:index] as *const T;
            &*obj
        }
    }

    fn first_object(&self) -> Option<&T> {
        unsafe {
            let obj = msg_send![self firstObject] as *const T;
            obj.as_ref()
        }
    }

    fn last_object(&self) -> Option<&T> {
        unsafe {
            let obj = msg_send![self lastObject] as *const T;
            obj.as_ref()
        }
    }

    fn object_enumerator(&self) -> NSEnumerator<T> {
        unsafe {
            let result = msg_send![self objectEnumerator];
            NSEnumerator::from_ptr(result)
        }
    }

    unsafe fn from_refs(refs: &[&T]) -> Id<Self> {
        let cls = class::<Self>();
        let obj = msg_send![cls alloc];
        let obj = msg_send![obj initWithObjects:refs.as_ptr() count:refs.len()];
        Id::from_retained_ptr(obj as *mut Self)
    }

    fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self> {
        let refs = vec.as_refs_slice();
        unsafe {
            INSArray::from_refs(refs)
        }
    }

    fn objects_in_range(&self, start: uint, len: uint) -> Vec<&T> {
        let vec: Vec<*mut T> = Vec::from_elem(len, RawPtr::null());
        let range = NSRange { location: start, length: len };
        unsafe {
            msg_send![self getObjects:vec.as_ptr() range:range];
            vec.map_in_place(|ptr| &*ptr)
        }
    }

    fn to_vec(&self) -> Vec<&T> {
        self.objects_in_range(0, self.count())
    }

    fn into_vec(array: Id<Self>) -> Vec<Id<T, O>> {
        let vec = array.to_vec();
        unsafe {
            vec.into_id_vec()
        }
    }
}

pub trait INSOwnedArray<T: INSObject> : INSArray<T, Owned> {
    fn mut_object_at(&mut self, index: uint) -> &mut T {
        unsafe {
            let result = msg_send![self objectAtIndex:index] as *mut T;
            &mut *result
        }
    }
}

pub trait INSSharedArray<T: INSObject> : INSArray<T, Shared> {
    fn shared_object_at(&self, index: uint) -> ShareId<T> {
        let obj = self.object_at(index);
        unsafe {
            Id::from_ptr(obj as *const _ as *mut T)
        }
    }

    fn from_slice(slice: &[ShareId<T>]) -> Id<Self> {
        let refs = slice.as_refs_slice();
        unsafe {
            INSArray::from_refs(refs)
        }
    }

    fn to_shared_vec(&self) -> Vec<ShareId<T>> {
        let vec = self.to_vec();
        unsafe {
            vec.into_id_vec()
        }
    }
}

pub struct NSArray<T, O = Owned> {
    nocopy: NoCopy,
}

object_impl!(NSArray<T, O>)

impl<T: INSObject, O: Ownership> INSArray<T, O> for NSArray<T, O> { }

impl<T: INSObject> INSOwnedArray<T> for NSArray<T, Owned> { }

impl<T: INSObject> INSSharedArray<T> for NSArray<T, Shared> { }

impl<T> INSCopying<NSSharedArray<T>> for NSArray<T, Shared> { }

impl<T> INSMutableCopying<NSMutableSharedArray<T>> for NSArray<T, Shared> { }

impl<T: INSObject, O: Ownership> Index<uint, T> for NSArray<T, O> {
    fn index(&self, index: &uint) -> &T {
        self.object_at(*index)
    }
}

pub type NSSharedArray<T> = NSArray<T, Shared>;

pub trait INSMutableArray<T: INSObject, O: Ownership> : INSArray<T, O> {
    fn add_object(&mut self, obj: Id<T, O>) {
        unsafe {
            msg_send![self addObject:obj];
        }
    }

    fn insert_object_at(&mut self, index: uint, obj: Id<T, O>) {
        unsafe {
            msg_send![self insertObject:obj atIndex:index];
        }
    }

    fn replace_object_at(&mut self, index: uint, obj: Id<T, O>) -> Id<T, O> {
        let old_obj = unsafe {
            let obj = self.object_at(index);
            Id::from_ptr(obj as *const _ as *mut T)
        };
        unsafe {
            msg_send![self replaceObjectAtIndex:index withObject:obj];
        }
        old_obj
    }

    fn remove_object_at(&mut self, index: uint) -> Id<T, O> {
        let obj = unsafe {
            let obj = self.object_at(index);
            Id::from_ptr(obj as *const _ as *mut T)
        };
        unsafe {
            msg_send![self removeObjectAtIndex:index];
        }
        obj
    }

    fn remove_last_object(&mut self) -> Id<T, O> {
        let obj = self.last_object().map(|obj| unsafe {
            Id::from_ptr(obj as *const _ as *mut T)
        });
        unsafe {
            msg_send![self removeLastObject];
        }
        // removeLastObject would have failed if the array is empty,
        // so we know this won't be None
        obj.unwrap()
    }

    fn remove_all_objects(&mut self) {
        unsafe {
            msg_send![self removeAllObjects];
        }
    }

    fn sort_with_context<C>(&mut self,
            compare: extern fn(&T, &T, Option<&C>) -> NSComparisonResult,
            context: Option<&C>) {
        unsafe {
            msg_send![self sortUsingFunction:compare context:context];
        }
    }
}

pub struct NSMutableArray<T, O = Owned> {
    nocopy: NoCopy,
}

object_impl!(NSMutableArray<T, O>)

impl<T: INSObject, O: Ownership> INSArray<T, O> for NSMutableArray<T, O> { }

impl<T: INSObject> INSOwnedArray<T> for NSMutableArray<T, Owned> { }

impl<T: INSObject> INSSharedArray<T> for NSMutableArray<T, Shared> { }

impl<T: INSObject, O: Ownership> INSMutableArray<T, O> for NSMutableArray<T, O> { }

impl<T> INSCopying<NSSharedArray<T>> for NSMutableArray<T, Shared> { }

impl<T> INSMutableCopying<NSMutableSharedArray<T>> for NSMutableArray<T, Shared> { }

impl<T: INSObject, O: Ownership> Index<uint, T> for NSMutableArray<T, O> {
    fn index(&self, index: &uint) -> &T {
        self.object_at(*index)
    }
}

pub type NSMutableSharedArray<T> = NSMutableArray<T, Shared>;

#[cfg(test)]
mod tests {
    use objc::{Id};
    use {INSObject, INSString, NSObject, NSString};
    use super::{
        INSArray, INSMutableArray,
        NSArray, NSMutableArray, NSComparisonResult
    };

    fn sample_array(len: uint) -> Id<NSArray<NSObject>> {
        let vec: Vec<Id<NSObject>> = Vec::from_fn(len, |_| INSObject::new());
        INSArray::from_vec(vec)
    }

    #[test]
    fn test_count() {
        let empty_array: Id<NSArray<NSObject>> = INSObject::new();
        assert!(empty_array.count() == 0);

        let array = sample_array(4);
        assert!(array.count() == 4);
    }

    #[test]
    fn test_object_at() {
        let array = sample_array(4);
        assert!(array.object_at(0) != array.object_at(3));
        assert!(array.first_object().unwrap() == array.object_at(0));
        assert!(array.last_object().unwrap() == array.object_at(3));

        let empty_array: Id<NSArray<NSObject>> = INSObject::new();
        assert!(empty_array.first_object().is_none());
        assert!(empty_array.last_object().is_none());
    }

    #[test]
    fn test_object_enumerator() {
        let array = sample_array(4);

        assert!(array.object_enumerator().count() == 4);
        assert!(array.object_enumerator()
                     .enumerate()
                     .all(|(i, obj)| obj == array.object_at(i)));
    }

    #[test]
    fn test_objects_in_range() {
        let array = sample_array(4);

        let middle_objs = array.objects_in_range(1, 2);
        assert!(middle_objs.len() == 2);
        assert!(middle_objs[0] == array.object_at(1));
        assert!(middle_objs[1] == array.object_at(2));

        let empty_objs = array.objects_in_range(1, 0);
        assert!(empty_objs.len() == 0);

        let all_objs = array.objects_in_range(0, 4);
        assert!(all_objs.len() == 4);
    }

    #[test]
    fn test_into_vec() {
        let array = sample_array(4);

        let vec = INSArray::into_vec(array);
        assert!(vec.len() == 4);
    }

    #[test]
    fn test_add_object() {
        let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
        let obj: Id<NSObject> = INSObject::new();
        array.add_object(obj);

        assert!(array.count() == 1);
        assert!(array.object_at(0) == array.object_at(0));

        let obj: Id<NSObject> = INSObject::new();
        array.insert_object_at(0, obj);
        assert!(array.count() == 2);
    }

    #[test]
    fn test_replace_object() {
        let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
        let obj: Id<NSObject> = INSObject::new();
        array.add_object(obj);

        let obj: Id<NSObject> = INSObject::new();
        let old_obj = array.replace_object_at(0, obj);
        assert!(&*old_obj != array.object_at(0));
    }

    #[test]
    fn test_remove_object() {
        let mut array: Id<NSMutableArray<NSObject>> = INSObject::new();
        for _ in range(0u, 4) {
            let obj: Id<NSObject> = INSObject::new();
            array.add_object(obj);
        }

        array.remove_object_at(1);
        assert!(array.count() == 3);

        array.remove_last_object();
        assert!(array.count() == 2);

        array.remove_all_objects();
        assert!(array.count() == 0);
    }

    #[test]
    fn test_sort() {
        let strings: Vec<Id<NSString>> = vec![
            INSString::from_str("hello"),
            INSString::from_str("hi"),
        ];
        let mut strings: Id<NSMutableArray<_>> = INSArray::from_vec(strings);

        extern fn compare_strings_by_len(s1: &NSString, s2: &NSString,
                _: Option<&()>) -> NSComparisonResult {
            let s1_len = s1.as_str().len();
            let s2_len = s2.as_str().len();
            if s1_len < s2_len {
                NSComparisonResult::Ascending
            } else if s1_len == s2_len {
                NSComparisonResult::Same
            } else {
                NSComparisonResult::Descending
            }
        }

        strings.sort_with_context(compare_strings_by_len, None);
        assert!(strings[0].as_str() == "hi");
        assert!(strings[1].as_str() == "hello");
    }
}
<|endoftext|>


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

//! Various alignment algorithms.

pub mod pairwise;

/// Alignment operations (Match, Subst, Del and Ins).
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AlignmentOperation {
    Match,
    Subst,
    Del,
    Ins
}

/// An alignment, consisting of a score, a start in sequence y, a start in sequence x, a length
/// and its edit operations (see alignment::pairwise for meaning of x and y).
#[derive(Debug)]
pub struct Alignment {
    pub score: i32,
    pub ystart: usize,
    pub xstart: usize,
    pub xlen: usize,
    pub operations: Vec<AlignmentOperation>
}

impl Alignment {
    /// Calculate the cigar string.
    pub fn cigar(&self, hard_clip: bool) -> String {
        let add_op = |op, k, cigar: &mut String| {
            cigar.push_str(&format!("{}{}", k, match op {
                AlignmentOperation::Match => "=",
                AlignmentOperation::Subst => "X",
                AlignmentOperation::Del => "D",
                AlignmentOperation::Ins => "I",
            }));
        };

        let op_len = |op: AlignmentOperation| {
            (op == AlignmentOperation::Match || op == AlignmentOperation::Subst || op == AlignmentOperation::Ins) as usize
        };

        let clip_str = if hard_clip {"H"} else {"S"};

        let mut cigar = String::new();

        if !self.operations.is_empty() {
            if self.xstart > 0 {
                cigar.push_str(&format!("{}{}", self.xstart, clip_str));
            }

            let mut last = self.operations[0];
            let mut k = 1;
            let mut alen = op_len(last);
            for &op in self.operations[1..].iter() {
                if op == last {
                    k += 1;
                } else {
                    add_op(last, k, &mut cigar);
                    k = 1;
                }
                last = op;
                alen += op_len(op);
            }
            add_op(last, k, &mut cigar);

            let clip = self.xlen - alen;
            if clip > 0 {
                cigar.push_str(&format!("{}{}", clip, clip_str));
            }
        }
        else {
            cigar.push_str(&format!("{}{}", self.xlen, clip_str));
        }

        cigar
    }

    /// Return the pretty formatted alignment as a string.
    pub fn pretty(&self, x: &[u8], y: &[u8]) -> String {
        let mut x_pretty = String::new();
        let mut y_pretty = String::new();
        let mut inb_pretty = String::new();

        if !self.operations.is_empty() {
            let mut x_add = &[u8];
            let mut y_add = String::new();
            let mut inb_add = String::new();

            for i in 1..self.operations.len() {
                match self.operations[i] {
                    AlignmentOperation::Match => {
                        x_add = &x[i];
                        inb_add = "|";
                        y_add = y[i];
                    },
                    AlignmentOperation::Subst => {
                        x_add = x[i];
                        inb_add = " ";
                        y_add = y[i];
                    },
                    AlignmentOperation::Del => {
                        x_add = "-";
                        inb_add = " ";
                        y_add = y[i];
                    },
                    AlignmentOperation::Ins => {
                        x_add = x[i];
                        inb_add = " ";
                        y_add = "-";
                    },
                }

                x_pretty.push_str(x_add);
                inb_pretty.push_str(inb_add);
                y_pretty.push_str(y_add);
            }
        }

        format!("{}\n{}\n{}", x_pretty, inb_pretty, y_pretty)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::AlignmentOperation::*;

    #[test]
    fn test_cigar() {
        let alignment = Alignment { score: 5, xstart: 3, ystart: 0, xlen: 10, operations: vec![Match, Match, Match, Subst, Ins, Ins, Del, Del] };
        assert_eq!(alignment.cigar(false), "3S3=1X2I2D4S");
    }
}
<commit_msg>working on pretty alignments - add trailing "-" and substrings<commit_after>// Copyright 2014 Johannes Köster.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Various alignment algorithms.

pub mod pairwise;

/// Alignment operations (Match, Subst, Del and Ins).
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AlignmentOperation {
    Match,
    Subst,
    Del,
    Ins
}

/// An alignment, consisting of a score, a start in sequence y, a start in sequence x, a length
/// and its edit operations (see alignment::pairwise for meaning of x and y).
#[derive(Debug)]
pub struct Alignment {
    pub score: i32,
    pub ystart: usize,
    pub xstart: usize,
    pub xlen: usize,
    pub operations: Vec<AlignmentOperation>
}

impl Alignment {
    /// Calculate the cigar string.
    pub fn cigar(&self, hard_clip: bool) -> String {
        let add_op = |op, k, cigar: &mut String| {
            cigar.push_str(&format!("{}{}", k, match op {
                AlignmentOperation::Match => "=",
                AlignmentOperation::Subst => "X",
                AlignmentOperation::Del => "D",
                AlignmentOperation::Ins => "I",
            }));
        };

        let op_len = |op: AlignmentOperation| {
            (op == AlignmentOperation::Match || op == AlignmentOperation::Subst || op == AlignmentOperation::Ins) as usize
        };

        let clip_str = if hard_clip {"H"} else {"S"};

        let mut cigar = String::new();

        if !self.operations.is_empty() {
            if self.xstart > 0 {
                cigar.push_str(&format!("{}{}", self.xstart, clip_str));
            }

            let mut last = self.operations[0];
            let mut k = 1;
            let mut alen = op_len(last);
            for &op in self.operations[1..].iter() {
                if op == last {
                    k += 1;
                } else {
                    add_op(last, k, &mut cigar);
                    k = 1;
                }
                last = op;
                alen += op_len(op);
            }
            add_op(last, k, &mut cigar);

            let clip = self.xlen - alen;
            if clip > 0 {
                cigar.push_str(&format!("{}{}", clip, clip_str));
            }
        }
        else {
            cigar.push_str(&format!("{}{}", self.xlen, clip_str));
        }

        cigar
    }

    /// Return the pretty formatted alignment as a String.
    pub fn pretty(&self, x: &[u8], y: &[u8]) -> String {
        let mut x_pretty = String::new();
        let mut y_pretty = String::new();
        let mut inb_pretty = String::new();

        if !self.operations.is_empty() {
            let mut x_i : usize = self.xstart;
            let mut y_i : usize = self.ystart;

            if x_i > y_i {
                let diff = x_i - y_i;
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                for _ in 0..diff {
                    y_pretty.push('-');
                }
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[diff..y_i])));
            } else if x_i < y_i {
                let diff = y_i - x_i;
                for _ in 0..diff {
                    x_pretty.push('-');
                }
                // x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[diff..x_i])));
                // y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..y_i])));
            } else {
                for i in 0..x_i {
                    x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[i]])));
                    y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[i]])));
                }
            }

            for _ in 0..x_pretty.len() {
                inb_pretty.push(' ');
            }

            for i in 0..self.operations.len() {
                match self.operations[i] {
                    AlignmentOperation::Match => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push_str("|");

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Subst => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Del => {
                        x_pretty.push('-');

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Ins => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push('-');
                    },
                }
            }

            for i in x_i..x.len() {
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[i]])));
            }
            for i in y_i..y.len() {
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[i]])));
            }
            if x_pretty.len() > y_pretty.len() {
                for _ in y_pretty.len()..x_pretty.len() {
                    y_pretty.push('-');
                }
            } else {
                for _ in x_pretty.len()..y_pretty.len() {
                    x_pretty.push('-');
                }
            }
        }

        format!("{}\n{}\n{}", x_pretty, inb_pretty, y_pretty)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use super::AlignmentOperation::*;

    #[test]
    fn test_cigar() {
        let alignment = Alignment { score: 5, xstart: 3, ystart: 0, xlen: 10, operations: vec![Match, Match, Match, Subst, Ins, Ins, Del, Del] };
        assert_eq!(alignment.cigar(false), "3S3=1X2I2D4S");
    }
}
<|endoftext|>


<commit_before>use crate::gen::block::Block;
use crate::gen::builtin::Builtins;
use crate::gen::include::Includes;
use crate::gen::Opt;
use crate::syntax::Types;
use std::cell::RefCell;
use std::fmt::{self, Arguments, Write};

pub(crate) struct OutFile<'a> {
    pub header: bool,
    pub opt: &'a Opt,
    pub types: &'a Types<'a>,
    pub include: Includes<'a>,
    pub builtin: Builtins<'a>,
    content: RefCell<Content<'a>>,
}

#[derive(Default)]
pub struct Content<'a> {
    bytes: String,
    blocks: Vec<Block<'a>>,
    section_pending: bool,
    blocks_pending: usize,
}

impl<'a> OutFile<'a> {
    pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self {
        OutFile {
            header,
            opt,
            types,
            include: Includes::new(),
            builtin: Builtins::new(),
            content: RefCell::new(Content::new()),
        }
    }

    // Write a blank line if the preceding section had any contents.
    pub fn next_section(&mut self) {
        self.content.get_mut().next_section();
    }

    pub fn begin_block(&mut self, block: Block<'a>) {
        self.content.get_mut().begin_block(block);
    }

    pub fn end_block(&mut self, block: Block<'a>) {
        self.content.get_mut().end_block(block);
    }

    pub fn write_fmt(&self, args: Arguments) {
        let content = &mut *self.content.borrow_mut();
        Write::write_fmt(content, args).unwrap();
    }

    pub fn content(&self) -> Vec<u8> {
        let include = &self.include.content.bytes;
        let builtin = &self.builtin.content.bytes;
        let content = &self.content.borrow().bytes;
        let len = include.len() + builtin.len() + content.len() + 2;
        let mut out = String::with_capacity(len);
        out.push_str(include);
        if !out.is_empty() && !builtin.is_empty() {
            out.push('\n');
        }
        out.push_str(builtin);
        if !out.is_empty() && !content.is_empty() {
            out.push('\n');
        }
        out.push_str(content);
        if out.is_empty() {
            out.push_str("// empty\n");
        }
        out.into_bytes()
    }
}

impl<'a> Write for Content<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.write(s);
        Ok(())
    }
}

impl<'a> PartialEq for Content<'a> {
    fn eq(&self, _other: &Content) -> bool {
        true
    }
}

impl<'a> Content<'a> {
    fn new() -> Self {
        Content::default()
    }

    pub fn next_section(&mut self) {
        self.section_pending = true;
    }

    pub fn begin_block(&mut self, block: Block<'a>) {
        self.blocks.push(block);
        self.blocks_pending += 1;
    }

    pub fn end_block(&mut self, block: Block<'a>) {
        let begin_block = self.blocks.pop().unwrap();
        let end_block = block;
        assert_eq!(begin_block, end_block);

        if self.blocks_pending > 0 {
            self.blocks_pending -= 1;
        } else {
            Block::write_end(block, &mut self.bytes);
            self.section_pending = true;
        }
    }

    pub fn write_fmt(&mut self, args: Arguments) {
        Write::write_fmt(self, args).unwrap();
    }

    fn write(&mut self, b: &str) {
        if !b.is_empty() {
            if self.blocks_pending > 0 {
                if !self.bytes.is_empty() {
                    self.bytes.push('\n');
                }
                let pending = self.blocks.len() - self.blocks_pending..;
                for block in &self.blocks[pending] {
                    Block::write_begin(*block, &mut self.bytes);
                }
            } else if self.section_pending && !self.bytes.is_empty() {
                self.bytes.push('\n');
            }
            self.bytes.push_str(b);
            self.section_pending = false;
            self.blocks_pending = 0;
        }
    }
}
<commit_msg>Recognize identical block being closed and opened<commit_after>use crate::gen::block::Block;
use crate::gen::builtin::Builtins;
use crate::gen::include::Includes;
use crate::gen::Opt;
use crate::syntax::Types;
use std::cell::RefCell;
use std::fmt::{self, Arguments, Write};

pub(crate) struct OutFile<'a> {
    pub header: bool,
    pub opt: &'a Opt,
    pub types: &'a Types<'a>,
    pub include: Includes<'a>,
    pub builtin: Builtins<'a>,
    content: RefCell<Content<'a>>,
}

#[derive(Default)]
pub struct Content<'a> {
    bytes: String,
    blocks: Vec<BlockBoundary<'a>>,
    section_pending: bool,
    blocks_pending: usize,
}

#[derive(Copy, Clone, PartialEq, Debug)]
enum BlockBoundary<'a> {
    Begin(Block<'a>),
    End(Block<'a>),
}

impl<'a> OutFile<'a> {
    pub fn new(header: bool, opt: &'a Opt, types: &'a Types) -> Self {
        OutFile {
            header,
            opt,
            types,
            include: Includes::new(),
            builtin: Builtins::new(),
            content: RefCell::new(Content::new()),
        }
    }

    // Write a blank line if the preceding section had any contents.
    pub fn next_section(&mut self) {
        self.content.get_mut().next_section();
    }

    pub fn begin_block(&mut self, block: Block<'a>) {
        self.content.get_mut().begin_block(block);
    }

    pub fn end_block(&mut self, block: Block<'a>) {
        self.content.get_mut().end_block(block);
    }

    pub fn write_fmt(&self, args: Arguments) {
        let content = &mut *self.content.borrow_mut();
        Write::write_fmt(content, args).unwrap();
    }

    pub fn content(&mut self) -> Vec<u8> {
        self.flush();
        let include = &self.include.content.bytes;
        let builtin = &self.builtin.content.bytes;
        let content = &self.content.get_mut().bytes;
        let len = include.len() + builtin.len() + content.len() + 2;
        let mut out = String::with_capacity(len);
        out.push_str(include);
        if !out.is_empty() && !builtin.is_empty() {
            out.push('\n');
        }
        out.push_str(builtin);
        if !out.is_empty() && !content.is_empty() {
            out.push('\n');
        }
        out.push_str(content);
        if out.is_empty() {
            out.push_str("// empty\n");
        }
        out.into_bytes()
    }

    fn flush(&mut self) {
        self.include.content.flush();
        self.builtin.content.flush();
        self.content.get_mut().flush();
    }
}

impl<'a> Write for Content<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.write(s);
        Ok(())
    }
}

impl<'a> PartialEq for Content<'a> {
    fn eq(&self, _other: &Content) -> bool {
        true
    }
}

impl<'a> Content<'a> {
    fn new() -> Self {
        Content::default()
    }

    pub fn next_section(&mut self) {
        self.section_pending = true;
    }

    pub fn begin_block(&mut self, block: Block<'a>) {
        self.push_block_boundary(BlockBoundary::Begin(block));
    }

    pub fn end_block(&mut self, block: Block<'a>) {
        self.push_block_boundary(BlockBoundary::End(block));
    }

    pub fn write_fmt(&mut self, args: Arguments) {
        Write::write_fmt(self, args).unwrap();
    }

    fn write(&mut self, b: &str) {
        if !b.is_empty() {
            if self.blocks_pending > 0 {
                self.flush_blocks();
            }
            if self.section_pending && !self.bytes.is_empty() {
                self.bytes.push('\n');
            }
            self.bytes.push_str(b);
            self.section_pending = false;
            self.blocks_pending = 0;
        }
    }

    fn push_block_boundary(&mut self, boundary: BlockBoundary<'a>) {
        if self.blocks_pending > 0 && boundary == self.blocks.last().unwrap().rev() {
            self.blocks.pop();
            self.blocks_pending -= 1;
        } else {
            self.blocks.push(boundary);
            self.blocks_pending += 1;
        }
    }

    fn flush(&mut self) {
        if self.blocks_pending > 0 {
            self.flush_blocks();
        }
    }

    fn flush_blocks(&mut self) {
        self.section_pending = !self.bytes.is_empty();
        let mut read = self.blocks.len() - self.blocks_pending;
        let mut write = read;

        while read < self.blocks.len() {
            match self.blocks[read] {
                BlockBoundary::Begin(begin_block) => {
                    if self.section_pending {
                        self.bytes.push('\n');
                        self.section_pending = false;
                    }
                    Block::write_begin(begin_block, &mut self.bytes);
                    self.blocks[write] = BlockBoundary::Begin(begin_block);
                    write += 1;
                }
                BlockBoundary::End(end_block) => {
                    write = write.checked_sub(1).unwrap();
                    let begin_block = self.blocks[write];
                    assert_eq!(begin_block, BlockBoundary::Begin(end_block));
                    Block::write_end(end_block, &mut self.bytes);
                    self.section_pending = true;
                }
            }
            read += 1;
        }

        self.blocks.truncate(write);
    }
}

impl<'a> BlockBoundary<'a> {
    fn rev(self) -> BlockBoundary<'a> {
        match self {
            BlockBoundary::Begin(block) => BlockBoundary::End(block),
            BlockBoundary::End(block) => BlockBoundary::Begin(block),
        }
    }
}
<|endoftext|>