<commit_before>use entities::*;
use business::error::RepoError;
use diesel;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use std::result;
use business::db::Db;
use super::models;
use super::schema;
use diesel::result::{DatabaseErrorKind, Error as DieselError};

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

fn unset_current_on_all_entries(
    con: &&mut SqliteConnection,
    id: &str,
) -> result::Result<usize, diesel::result::Error> {
    use self::schema::entries::dsl;
    diesel::update(
        dsl::entries
            .filter(dsl::id.eq(id))
            .filter(dsl::current.eq(true)),
    ).set(dsl::current.eq(false))
        .execute(*con)
}

impl Db for SqliteConnection {
    fn create_entry(&mut self, e: &Entry) -> Result<()> {
        let new_entry = models::Entry::from(e.clone());
        let cat_rels: Vec<_> = e.categories
            .iter()
            .cloned()
            .map(|category_id| models::EntryCategoryRelation {
                entry_id: e.id.clone(),
                entry_version: e.version as i64,
                category_id,
            })
            .collect();
        self.transaction::<_, diesel::result::Error, _>(|| {
            unset_current_on_all_entries(&self, &e.id)?;
            diesel::insert_into(schema::entries::table)
                .values(&new_entry)
                .execute(self)?;
            diesel::insert_into(schema::entry_category_relations::table)
                //WHERE NOT EXISTS
                .values(&cat_rels)
                .execute(self)?;
            Ok(())
        })?;
        Ok(())
    }
    fn create_tag_if_it_does_not_exist(&mut self, t: &Tag) -> Result<()> {
        let res = diesel::insert_into(schema::tags::table)
            .values(&models::Tag::from(t.clone()))
            .execute(self);
        if let Err(err) = res {
            match err {
                DieselError::DatabaseError(db_err, _) => {
                    match db_err {
                        DatabaseErrorKind::UniqueViolation => {
                            // that's ok :)
                        }
                        _ => {
                            return Err(err.into());
                        }
                    }
                }
                _ => {
                    return Err(err.into());
                }
            }
        }
        Ok(())
    }
    fn create_user(&mut self, u: &User) -> Result<()> {
        diesel::insert_into(schema::users::table)
            .values(&models::User::from(u.clone()))
            .execute(self)?;
        Ok(())
    }
    fn create_comment(&mut self, c: &Comment) -> Result<()> {
        diesel::insert_into(schema::comments::table)
            .values(&models::Comment::from(c.clone()))
            .execute(self)?;
        Ok(())
    }
    fn create_rating(&mut self, r: &Rating) -> Result<()> {
        diesel::insert_into(schema::ratings::table)
            .values(&models::Rating::from(r.clone()))
            .execute(self)?;
        Ok(())
    }
    fn create_bbox_subscription(&mut self, sub: &BboxSubscription) -> Result<()> {
        diesel::insert_into(schema::bbox_subscriptions::table)
            .values(&models::BboxSubscription::from(sub.clone()))
            .execute(self)?;
        Ok(())
    }
    fn all_users(&self) -> Result<Vec<User>> {
        use self::schema::users::dsl;
        Ok(dsl::users
            .load::<models::User>(self)?
            .into_iter()
            .map(User::from)
            .collect())
    }
    fn all_bbox_subscriptions(&self) -> Result<Vec<BboxSubscription>> {
        use self::schema::bbox_subscriptions::dsl;
        Ok(dsl::bbox_subscriptions
            .load::<models::BboxSubscription>(self)?
            .into_iter()
            .map(BboxSubscription::from)
            .collect())
    }
    fn confirm_email_address(&mut self, username: &str) -> Result<User> {
        use self::schema::users::dsl;
        diesel::update(dsl::users.find(username))
            .set(dsl::email_confirmed.eq(true))
            .execute(self)?;
        Ok(self.get_user(username)?)
    }
    fn delete_bbox_subscription(&mut self, id: &str) -> Result<()> {
        use self::schema::bbox_subscriptions::dsl;
        diesel::delete(dsl::bbox_subscriptions.find(id)).execute(self)?;
        Ok(())
    }
    fn delete_user(&mut self, user: &str) -> Result<()> {
        use self::schema::users::dsl::*;
        diesel::delete(users.find(user)).execute(self)?;
        Ok(())
    }

    fn get_entry(&self, e_id: &str) -> Result<Entry> {
        use self::schema::entries::dsl as e_dsl;
        use self::schema::entry_category_relations::dsl as e_c_dsl;
        use self::schema::entry_tag_relations::dsl as e_t_dsl;

        let models::Entry {
            id,
            osm_node,
            created,
            version,
            title,
            description,
            lat,
            lng,
            street,
            zip,
            city,
            country,
            email,
            telephone,
            homepage,
            license,
            ..
        } = e_dsl::entries
            .filter(e_dsl::id.eq(e_id))
            .filter(e_dsl::current.eq(true))
            .first(self)?;

        let categories = e_c_dsl::entry_category_relations
            .filter(e_c_dsl::entry_id.eq(&id))
            .load::<models::EntryCategoryRelation>(self)?
            .into_iter()
            .map(|r| r.category_id)
            .collect();

        let tags = e_t_dsl::entry_tag_relations
            .filter(e_t_dsl::entry_id.eq(&id))
            .load::<models::EntryTagRelation>(self)?
            .into_iter()
            .map(|r| r.tag_id)
            .collect();

        Ok(Entry {
            id,
            osm_node: osm_node.map(|x| x as u64),
            created: created as u64,
            version: version as u64,
            title,
            description,
            lat: lat as f64,
            lng: lng as f64,
            street,
            zip,
            city,
            country,
            email,
            telephone,
            homepage,
            categories,
            tags,
            license,
        })
    }

    fn get_entries_by_bbox(&self, bbox: &Bbox) -> Result<Vec<Entry>> {
        use self::schema::entries::dsl as e_dsl;
        use self::schema::entry_category_relations::dsl as e_c_dsl;
        use self::schema::entry_tag_relations::dsl as e_t_dsl;

        let entries: Vec<models::Entry> = e_dsl::entries
            .filter(e_dsl::current.eq(true))
            .filter(e_dsl::lat.between(bbox.south_west.lat, bbox.north_east.lat))
            .filter(e_dsl::lng.between(bbox.south_west.lng, bbox.north_east.lng))
            .load(self)?;

        let cat_rels =
            e_c_dsl::entry_category_relations.load::<models::EntryCategoryRelation>(self)?;

        let tag_rels = e_t_dsl::entry_tag_relations.load::<models::EntryTagRelation>(self)?;

        Ok(entries
            .into_iter()
            .map(|e| {
                let cats = cat_rels
                    .iter()
                    .filter(|r| r.entry_id == e.id)
                    .filter(|r| r.entry_version == e.version)
                    .map(|r| &r.category_id)
                    .cloned()
                    .collect();
                let tags = tag_rels
                    .iter()
                    .filter(|r| r.entry_id == e.id)
                    .filter(|r| r.entry_version == e.version)
                    .map(|r| &r.tag_id)
                    .cloned()
                    .collect();
                Entry {
                    id: e.id,
                    osm_node: e.osm_node.map(|x| x as u64),
                    created: e.created as u64,
                    version: e.version as u64,
                    title: e.title,
                    description: e.description,
                    lat: e.lat as f64,
                    lng: e.lng as f64,
                    street: e.street,
                    zip: e.zip,
                    city: e.city,
                    country: e.country,
                    email: e.email,
                    telephone: e.telephone,
                    homepage: e.homepage,
                    categories: cats,
                    tags: tags,
                    license: e.license,
                }
            })
            .collect())
    }

    fn get_user(&self, user_id: &str) -> Result<User> {
        use self::schema::users::dsl::*;
        let u: models::User = users.find(user_id).first(self)?;
        Ok(User::from(u))
    }

    fn all_entries(&self) -> Result<Vec<Entry>> {
        use self::schema::entries::dsl as e_dsl;
        use self::schema::entry_category_relations::dsl as e_c_dsl;
        use self::schema::entry_tag_relations::dsl as e_t_dsl;

        let entries: Vec<models::Entry> =
            e_dsl::entries.filter(e_dsl::current.eq(true)).load(self)?;

        let cat_rels =
            e_c_dsl::entry_category_relations.load::<models::EntryCategoryRelation>(self)?;

        let tag_rels = e_t_dsl::entry_tag_relations.load::<models::EntryTagRelation>(self)?;

        Ok(entries
            .into_iter()
            .map(|e| {
                let cats = cat_rels
                    .iter()
                    .filter(|r| r.entry_id == e.id)
                    .filter(|r| r.entry_version == e.version)
                    .map(|r| &r.category_id)
                    .cloned()
                    .collect();
                let tags = tag_rels
                    .iter()
                    .filter(|r| r.entry_id == e.id)
                    .filter(|r| r.entry_version == e.version)
                    .map(|r| &r.tag_id)
                    .cloned()
                    .collect();
                Entry {
                    id: e.id,
                    osm_node: e.osm_node.map(|x| x as u64),
                    created: e.created as u64,
                    version: e.version as u64,
                    title: e.title,
                    description: e.description,
                    lat: e.lat as f64,
                    lng: e.lng as f64,
                    street: e.street,
                    zip: e.zip,
                    city: e.city,
                    country: e.country,
                    email: e.email,
                    telephone: e.telephone,
                    homepage: e.homepage,
                    categories: cats,
                    tags: tags,
                    license: e.license,
                }
            })
            .collect())
    }
    fn all_categories(&self) -> Result<Vec<Category>> {
        use self::schema::categories::dsl::*;
        Ok(categories
            .load::<models::Category>(self)?
            .into_iter()
            .map(Category::from)
            .collect())
    }
    fn all_tags(&self) -> Result<Vec<Tag>> {
        use self::schema::tags::dsl::*;
        Ok(tags.load::<models::Tag>(self)?
            .into_iter()
            .map(Tag::from)
            .collect())
    }
    fn all_ratings(&self) -> Result<Vec<Rating>> {
        use self::schema::ratings::dsl::*;
        Ok(ratings
            .load::<models::Rating>(self)?
            .into_iter()
            .map(Rating::from)
            .collect())
    }
    fn all_comments(&self) -> Result<Vec<Comment>> {
        use self::schema::comments::dsl::*;
        Ok(comments
            .load::<models::Comment>(self)?
            .into_iter()
            .map(Comment::from)
            .collect())
    }

    fn update_entry(&mut self, entry: &Entry) -> Result<()> {
        let e = models::Entry::from(entry.clone());

        let cat_rels: Vec<_> = entry
            .categories
            .iter()
            .cloned()
            .map(|category_id| models::EntryCategoryRelation {
                entry_id: entry.id.clone(),
                entry_version: entry.version as i64,
                category_id,
            })
            .collect();

        self.transaction::<_, diesel::result::Error, _>(|| {
            unset_current_on_all_entries(&self, &e.id)?;
            diesel::insert_into(schema::entries::table)
                .values(&e)
                .execute(self)?;
            diesel::insert_into(schema::entry_category_relations::table)
                //WHERE NOT EXISTS
                .values(&cat_rels)
                .execute(self)?;
            Ok(())
        })?;
        Ok(())
    }

    fn import_multiple_entries(&mut self, new_entries: &[Entry]) -> Result<()> {
        let imports: Vec<_> = new_entries
            .into_iter()
            .map(|e| {
                let new_entry = models::Entry::from(e.clone());
                let cat_rels: Vec<_> = e.categories
                    .iter()
                    .cloned()
                    .map(|category_id| models::EntryCategoryRelation {
                        entry_id: e.id.clone(),
                        entry_version: e.version as i64,
                        category_id,
                    })
                    .collect();
                let tag_rels: Vec<_> = e.tags
                    .iter()
                    .map(|tag_id| models::EntryTagRelation {
                        entry_id: e.id.clone(),
                        entry_version: e.version as i64,
                        tag_id: tag_id.clone(),
                    })
                    .collect();
                (new_entry, cat_rels, tag_rels)
            })
            .collect();
        self.transaction::<_, diesel::result::Error, _>(|| {
            for (new_entry, cat_rels, tag_rels) in imports {
                unset_current_on_all_entries(&self, &new_entry.id)?;
                diesel::insert_into(schema::entries::table)
                    .values(&new_entry)
                    .execute(self)?;
                diesel::insert_into(schema::entry_category_relations::table)
                    .values(&cat_rels)
                    .execute(self)?;

                for r in &tag_rels {
                    let res = diesel::insert_into(schema::tags::table)
                        .values(&models::Tag {
                            id: r.tag_id.clone(),
                        })
                        .execute(self);
                    if let Err(err) = res {
                        match err {
                            DieselError::DatabaseError(db_err, _) => {
                                match db_err {
                                    DatabaseErrorKind::UniqueViolation => {
                                        // that's ok :)
                                    }
                                    _ => {
                                        return Err(err);
                                    }
                                }
                            }
                            _ => {
                                return Err(err);
                            }
                        }
                    }
                }
                diesel::insert_into(schema::entry_tag_relations::table)
                    .values(&tag_rels)
                    .execute(self)?;
            }
            Ok(())
        })?;
        Ok(())
    }
}
<commit_msg>fix(db): add tag relations<commit_after>use entities::*;
use business::error::RepoError;
use diesel;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use std::result;
use business::db::Db;
use super::models;
use super::schema;
use diesel::result::{DatabaseErrorKind, Error as DieselError};

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

fn unset_current_on_all_entries(
    con: &&mut SqliteConnection,
    id: &str,
) -> result::Result<usize, diesel::result::Error> {
    use self::schema::entries::dsl;
    diesel::update(
        dsl::entries
            .filter(dsl::id.eq(id))
            .filter(dsl::current.eq(true)),
    ).set(dsl::current.eq(false))
        .execute(*con)
}

impl Db for SqliteConnection {
    fn create_entry(&mut self, e: &Entry) -> Result<()> {
        let new_entry = models::Entry::from(e.clone());
        let cat_rels: Vec<_> = e.categories
            .iter()
            .cloned()
            .map(|category_id| models::EntryCategoryRelation {
                entry_id: e.id.clone(),
                entry_version: e.version as i64,
                category_id,
            })
            .collect();
        let tag_rels: Vec<_> = e.tags
            .iter()
            .cloned()
            .map(|tag_id| models::EntryTagRelation {
                entry_id: e.id.clone(),
                entry_version: e.version as i64,
                tag_id,
            })
            .collect();
        self.transaction::<_, diesel::result::Error, _>(|| {
            unset_current_on_all_entries(&self, &e.id)?;
            diesel::insert_into(schema::entries::table)
                .values(&new_entry)
                .execute(self)?;
            diesel::insert_into(schema::entry_category_relations::table)
                //WHERE NOT EXISTS
                .values(&cat_rels)
                .execute(self)?;
            diesel::insert_into(schema::entry_tag_relations::table)
                //WHERE NOT EXISTS
                .values(&tag_rels)
                .execute(self)?;
            Ok(())
        })?;
        Ok(())
    }
    fn create_tag_if_it_does_not_exist(&mut self, t: &Tag) -> Result<()> {
        let res = diesel::insert_into(schema::tags::table)
            .values(&models::Tag::from(t.clone()))
            .execute(self);
        if let Err(err) = res {
            match err {
                DieselError::DatabaseError(db_err, _) => {
                    match db_err {
                        DatabaseErrorKind::UniqueViolation => {
                            // that's ok :)
                        }
                        _ => {
                            return Err(err.into());
                        }
                    }
                }
                _ => {
                    return Err(err.into());
                }
            }
        }
        Ok(())
    }
    fn create_user(&mut self, u: &User) -> Result<()> {
        diesel::insert_into(schema::users::table)
            .values(&models::User::from(u.clone()))
            .execute(self)?;
        Ok(())
    }
    fn create_comment(&mut self, c: &Comment) -> Result<()> {
        diesel::insert_into(schema::comments::table)
            .values(&models::Comment::from(c.clone()))
            .execute(self)?;
        Ok(())
    }
    fn create_rating(&mut self, r: &Rating) -> Result<()> {
        diesel::insert_into(schema::ratings::table)
            .values(&models::Rating::from(r.clone()))
            .execute(self)?;
        Ok(())
    }
    fn create_bbox_subscription(&mut self, sub: &BboxSubscription) -> Result<()> {
        diesel::insert_into(schema::bbox_subscriptions::table)
            .values(&models::BboxSubscription::from(sub.clone()))
            .execute(self)?;
        Ok(())
    }
    fn all_users(&self) -> Result<Vec<User>> {
        use self::schema::users::dsl;
        Ok(dsl::users
            .load::<models::User>(self)?
            .into_iter()
            .map(User::from)
            .collect())
    }
    fn all_bbox_subscriptions(&self) -> Result<Vec<BboxSubscription>> {
        use self::schema::bbox_subscriptions::dsl;
        Ok(dsl::bbox_subscriptions
            .load::<models::BboxSubscription>(self)?
            .into_iter()
            .map(BboxSubscription::from)
            .collect())
    }
    fn confirm_email_address(&mut self, username: &str) -> Result<User> {
        use self::schema::users::dsl;
        diesel::update(dsl::users.find(username))
            .set(dsl::email_confirmed.eq(true))
            .execute(self)?;
        Ok(self.get_user(username)?)
    }
    fn delete_bbox_subscription(&mut self, id: &str) -> Result<()> {
        use self::schema::bbox_subscriptions::dsl;
        diesel::delete(dsl::bbox_subscriptions.find(id)).execute(self)?;
        Ok(())
    }
    fn delete_user(&mut self, user: &str) -> Result<()> {
        use self::schema::users::dsl::*;
        diesel::delete(users.find(user)).execute(self)?;
        Ok(())
    }

    fn get_entry(&self, e_id: &str) -> Result<Entry> {
        use self::schema::entries::dsl as e_dsl;
        use self::schema::entry_category_relations::dsl as e_c_dsl;
        use self::schema::entry_tag_relations::dsl as e_t_dsl;

        let models::Entry {
            id,
            osm_node,
            created,
            version,
            title,
            description,
            lat,
            lng,
            street,
            zip,
            city,
            country,
            email,
            telephone,
            homepage,
            license,
            ..
        } = e_dsl::entries
            .filter(e_dsl::id.eq(e_id))
            .filter(e_dsl::current.eq(true))
            .first(self)?;

        let categories = e_c_dsl::entry_category_relations
            .filter(e_c_dsl::entry_id.eq(&id))
            .load::<models::EntryCategoryRelation>(self)?
            .into_iter()
            .map(|r| r.category_id)
            .collect();

        let tags = e_t_dsl::entry_tag_relations
            .filter(e_t_dsl::entry_id.eq(&id))
            .load::<models::EntryTagRelation>(self)?
            .into_iter()
            .map(|r| r.tag_id)
            .collect();

        Ok(Entry {
            id,
            osm_node: osm_node.map(|x| x as u64),
            created: created as u64,
            version: version as u64,
            title,
            description,
            lat: lat as f64,
            lng: lng as f64,
            street,
            zip,
            city,
            country,
            email,
            telephone,
            homepage,
            categories,
            tags,
            license,
        })
    }

    fn get_entries_by_bbox(&self, bbox: &Bbox) -> Result<Vec<Entry>> {
        use self::schema::entries::dsl as e_dsl;
        use self::schema::entry_category_relations::dsl as e_c_dsl;
        use self::schema::entry_tag_relations::dsl as e_t_dsl;

        let entries: Vec<models::Entry> = e_dsl::entries
            .filter(e_dsl::current.eq(true))
            .filter(e_dsl::lat.between(bbox.south_west.lat, bbox.north_east.lat))
            .filter(e_dsl::lng.between(bbox.south_west.lng, bbox.north_east.lng))
            .load(self)?;

        let cat_rels =
            e_c_dsl::entry_category_relations.load::<models::EntryCategoryRelation>(self)?;

        let tag_rels = e_t_dsl::entry_tag_relations.load::<models::EntryTagRelation>(self)?;

        Ok(entries
            .into_iter()
            .map(|e| {
                let cats = cat_rels
                    .iter()
                    .filter(|r| r.entry_id == e.id)
                    .filter(|r| r.entry_version == e.version)
                    .map(|r| &r.category_id)
                    .cloned()
                    .collect();
                let tags = tag_rels
                    .iter()
                    .filter(|r| r.entry_id == e.id)
                    .filter(|r| r.entry_version == e.version)
                    .map(|r| &r.tag_id)
                    .cloned()
                    .collect();
                Entry {
                    id: e.id,
                    osm_node: e.osm_node.map(|x| x as u64),
                    created: e.created as u64,
                    version: e.version as u64,
                    title: e.title,
                    description: e.description,
                    lat: e.lat as f64,
                    lng: e.lng as f64,
                    street: e.street,
                    zip: e.zip,
                    city: e.city,
                    country: e.country,
                    email: e.email,
                    telephone: e.telephone,
                    homepage: e.homepage,
                    categories: cats,
                    tags: tags,
                    license: e.license,
                }
            })
            .collect())
    }

    fn get_user(&self, user_id: &str) -> Result<User> {
        use self::schema::users::dsl::*;
        let u: models::User = users.find(user_id).first(self)?;
        Ok(User::from(u))
    }

    fn all_entries(&self) -> Result<Vec<Entry>> {
        use self::schema::entries::dsl as e_dsl;
        use self::schema::entry_category_relations::dsl as e_c_dsl;
        use self::schema::entry_tag_relations::dsl as e_t_dsl;

        let entries: Vec<models::Entry> =
            e_dsl::entries.filter(e_dsl::current.eq(true)).load(self)?;

        let cat_rels =
            e_c_dsl::entry_category_relations.load::<models::EntryCategoryRelation>(self)?;

        let tag_rels = e_t_dsl::entry_tag_relations.load::<models::EntryTagRelation>(self)?;

        Ok(entries
            .into_iter()
            .map(|e| {
                let cats = cat_rels
                    .iter()
                    .filter(|r| r.entry_id == e.id)
                    .filter(|r| r.entry_version == e.version)
                    .map(|r| &r.category_id)
                    .cloned()
                    .collect();
                let tags = tag_rels
                    .iter()
                    .filter(|r| r.entry_id == e.id)
                    .filter(|r| r.entry_version == e.version)
                    .map(|r| &r.tag_id)
                    .cloned()
                    .collect();
                Entry {
                    id: e.id,
                    osm_node: e.osm_node.map(|x| x as u64),
                    created: e.created as u64,
                    version: e.version as u64,
                    title: e.title,
                    description: e.description,
                    lat: e.lat as f64,
                    lng: e.lng as f64,
                    street: e.street,
                    zip: e.zip,
                    city: e.city,
                    country: e.country,
                    email: e.email,
                    telephone: e.telephone,
                    homepage: e.homepage,
                    categories: cats,
                    tags: tags,
                    license: e.license,
                }
            })
            .collect())
    }
    fn all_categories(&self) -> Result<Vec<Category>> {
        use self::schema::categories::dsl::*;
        Ok(categories
            .load::<models::Category>(self)?
            .into_iter()
            .map(Category::from)
            .collect())
    }
    fn all_tags(&self) -> Result<Vec<Tag>> {
        use self::schema::tags::dsl::*;
        Ok(tags.load::<models::Tag>(self)?
            .into_iter()
            .map(Tag::from)
            .collect())
    }
    fn all_ratings(&self) -> Result<Vec<Rating>> {
        use self::schema::ratings::dsl::*;
        Ok(ratings
            .load::<models::Rating>(self)?
            .into_iter()
            .map(Rating::from)
            .collect())
    }
    fn all_comments(&self) -> Result<Vec<Comment>> {
        use self::schema::comments::dsl::*;
        Ok(comments
            .load::<models::Comment>(self)?
            .into_iter()
            .map(Comment::from)
            .collect())
    }

    fn update_entry(&mut self, entry: &Entry) -> Result<()> {
        let e = models::Entry::from(entry.clone());

        let cat_rels: Vec<_> = entry
            .categories
            .iter()
            .cloned()
            .map(|category_id| models::EntryCategoryRelation {
                entry_id: entry.id.clone(),
                entry_version: entry.version as i64,
                category_id,
            })
            .collect();

        let tag_rels: Vec<_> = entry
            .tags
            .iter()
            .cloned()
            .map(|tag_id| models::EntryTagRelation {
                entry_id: entry.id.clone(),
                entry_version: entry.version as i64,
                tag_id,
            })
            .collect();

        self.transaction::<_, diesel::result::Error, _>(|| {
            unset_current_on_all_entries(&self, &e.id)?;
            diesel::insert_into(schema::entries::table)
                .values(&e)
                .execute(self)?;
            diesel::insert_into(schema::entry_category_relations::table)
                //WHERE NOT EXISTS
                .values(&cat_rels)
                .execute(self)?;
            diesel::insert_into(schema::entry_tag_relations::table)
                //WHERE NOT EXISTS
                .values(&tag_rels)
                .execute(self)?;
            Ok(())
        })?;
        Ok(())
    }

    fn import_multiple_entries(&mut self, new_entries: &[Entry]) -> Result<()> {
        let imports: Vec<_> = new_entries
            .into_iter()
            .map(|e| {
                let new_entry = models::Entry::from(e.clone());
                let cat_rels: Vec<_> = e.categories
                    .iter()
                    .cloned()
                    .map(|category_id| models::EntryCategoryRelation {
                        entry_id: e.id.clone(),
                        entry_version: e.version as i64,
                        category_id,
                    })
                    .collect();
                let tag_rels: Vec<_> = e.tags
                    .iter()
                    .map(|tag_id| models::EntryTagRelation {
                        entry_id: e.id.clone(),
                        entry_version: e.version as i64,
                        tag_id: tag_id.clone(),
                    })
                    .collect();
                (new_entry, cat_rels, tag_rels)
            })
            .collect();
        self.transaction::<_, diesel::result::Error, _>(|| {
            for (new_entry, cat_rels, tag_rels) in imports {
                unset_current_on_all_entries(&self, &new_entry.id)?;
                diesel::insert_into(schema::entries::table)
                    .values(&new_entry)
                    .execute(self)?;
                diesel::insert_into(schema::entry_category_relations::table)
                    .values(&cat_rels)
                    .execute(self)?;

                for r in &tag_rels {
                    let res = diesel::insert_into(schema::tags::table)
                        .values(&models::Tag {
                            id: r.tag_id.clone(),
                        })
                        .execute(self);
                    if let Err(err) = res {
                        match err {
                            DieselError::DatabaseError(db_err, _) => {
                                match db_err {
                                    DatabaseErrorKind::UniqueViolation => {
                                        // that's ok :)
                                    }
                                    _ => {
                                        return Err(err);
                                    }
                                }
                            }
                            _ => {
                                return Err(err);
                            }
                        }
                    }
                }
                diesel::insert_into(schema::entry_tag_relations::table)
                    .values(&tag_rels)
                    .execute(self)?;
            }
            Ok(())
        })?;
        Ok(())
    }
}
<|endoftext|>


<commit_before>use objc::runtime::{Class, Object};
use objc::{Id, Message};

use NSString;

pub trait INSObject : Message {
    fn class_name() -> &'static str;

    fn class(&self) -> &Class {
        let obj = unsafe { &*(self as *const _ as *const Object) };
        obj.class()
    }

    fn hash_code(&self) -> uint {
        unsafe {
            msg_send![self, hash]
        }
    }

    fn is_equal<T: INSObject>(&self, other: &T) -> bool {
        let result: i8 = unsafe {
            msg_send![self, isEqual:other]
        };
        result != 0
    }

    fn description(&self) -> Id<NSString> {
        unsafe {
            let result: *mut NSString = msg_send![self, description];
            Id::from_ptr(result)
        }
    }

    fn is_kind_of(&self, cls: &Class) -> bool {
        let result: i8 = unsafe {
            msg_send![self, isKindOfClass:cls]
        };
        result != 0
    }

    fn as_object<T: INSObject>(&self) -> Option<&T> {
        let cls = class::<T>();
        if self.is_kind_of(cls) {
            let ptr = self as *const _ as *const T;
            Some(unsafe { &*ptr })
        } else {
            None
        }
    }

    fn new() -> Id<Self> {
        let cls = class::<Self>();
        unsafe {
            let obj: *mut Self = msg_send![cls, alloc];
            let obj: *mut Self = msg_send![obj, init];
            Id::from_retained_ptr(obj)
        }
    }
}

object_struct!(NSObject);

pub fn class<T: INSObject>() -> &'static Class {
    let name = <T as INSObject>::class_name();
    match Class::get(name) {
        Some(cls) => cls,
        None => panic!("Class {} not found", name),
    }
}

#[cfg(test)]
mod tests {
    use objc::Id;
    use {INSString, NSString};
    use super::{class, INSObject, NSObject};

    #[test]
    fn test_class_name() {
        assert!(<NSObject as INSObject>::class_name() == "NSObject");
    }

    #[test]
    fn test_class() {
        let obj: Id<NSObject> = INSObject::new();
        let cls = obj.class();
        assert!(cls.name() == "NSObject");
    }

    #[test]
    fn test_is_equal() {
        let obj1: Id<NSObject> = INSObject::new();
        assert!(obj1.is_equal(&*obj1));

        let obj2: Id<NSObject> = INSObject::new();
        assert!(!obj1.is_equal(&*obj2));
    }

    #[test]
    fn test_hash_code() {
        let obj: Id<NSObject> = INSObject::new();
        assert!(obj.hash_code() == obj.hash_code());
    }

    #[test]
    fn test_description() {
        let obj: Id<NSObject> = INSObject::new();
        let description = obj.description();
        let expected = format!("<NSObject: {:?}>", &*obj as *const NSObject);
        assert!(description.as_str() == expected.as_slice());
    }

    #[test]
    fn test_is_kind_of() {
        let obj: Id<NSObject> = INSObject::new();
        assert!(obj.is_kind_of(class::<NSObject>()));
        assert!(!obj.is_kind_of(class::<NSString>()));
    }

    #[test]
    fn test_as_object() {
        let obj: Id<NSObject> = INSObject::new();
        let as_str: Option<&NSString> = obj.as_object();
        assert!(as_str.is_none());

        let string: Id<NSString> = INSObject::new();
        let as_obj: Option<&NSObject> = string.as_object();
        assert!(as_obj.is_some());
    }
}
<commit_msg>Removed class method from INSObject.<commit_after>use objc::runtime::Class;
use objc::{Id, Message};

use NSString;

pub trait INSObject : Message {
    fn class_name() -> &'static str;

    fn hash_code(&self) -> uint {
        unsafe {
            msg_send![self, hash]
        }
    }

    fn is_equal<T: INSObject>(&self, other: &T) -> bool {
        let result: i8 = unsafe {
            msg_send![self, isEqual:other]
        };
        result != 0
    }

    fn description(&self) -> Id<NSString> {
        unsafe {
            let result: *mut NSString = msg_send![self, description];
            Id::from_ptr(result)
        }
    }

    fn is_kind_of(&self, cls: &Class) -> bool {
        let result: i8 = unsafe {
            msg_send![self, isKindOfClass:cls]
        };
        result != 0
    }

    fn as_object<T: INSObject>(&self) -> Option<&T> {
        let cls = class::<T>();
        if self.is_kind_of(cls) {
            let ptr = self as *const _ as *const T;
            Some(unsafe { &*ptr })
        } else {
            None
        }
    }

    fn new() -> Id<Self> {
        let cls = class::<Self>();
        unsafe {
            let obj: *mut Self = msg_send![cls, alloc];
            let obj: *mut Self = msg_send![obj, init];
            Id::from_retained_ptr(obj)
        }
    }
}

object_struct!(NSObject);

pub fn class<T: INSObject>() -> &'static Class {
    let name = <T as INSObject>::class_name();
    match Class::get(name) {
        Some(cls) => cls,
        None => panic!("Class {} not found", name),
    }
}

#[cfg(test)]
mod tests {
    use objc::Id;
    use {INSString, NSString};
    use super::{class, INSObject, NSObject};

    #[test]
    fn test_class_name() {
        assert!(<NSObject as INSObject>::class_name() == "NSObject");
    }

    #[test]
    fn test_is_equal() {
        let obj1: Id<NSObject> = INSObject::new();
        assert!(obj1.is_equal(&*obj1));

        let obj2: Id<NSObject> = INSObject::new();
        assert!(!obj1.is_equal(&*obj2));
    }

    #[test]
    fn test_hash_code() {
        let obj: Id<NSObject> = INSObject::new();
        assert!(obj.hash_code() == obj.hash_code());
    }

    #[test]
    fn test_description() {
        let obj: Id<NSObject> = INSObject::new();
        let description = obj.description();
        let expected = format!("<NSObject: {:?}>", &*obj as *const NSObject);
        assert!(description.as_str() == expected.as_slice());
    }

    #[test]
    fn test_is_kind_of() {
        let obj: Id<NSObject> = INSObject::new();
        assert!(obj.is_kind_of(class::<NSObject>()));
        assert!(!obj.is_kind_of(class::<NSString>()));
    }

    #[test]
    fn test_as_object() {
        let obj: Id<NSObject> = INSObject::new();
        let as_str: Option<&NSString> = obj.as_object();
        assert!(as_str.is_none());

        let string: Id<NSString> = INSObject::new();
        let as_obj: Option<&NSObject> = string.as_object();
        assert!(as_obj.is_some());
    }
}
<|endoftext|>


<commit_before>use crate::{Browser, BrowserOptions, Error, ErrorKind, Result};
use jni::objects::JValue;
pub use std::os::unix::process::ExitStatusExt;

/// Deal with opening of browsers on Android. Only [Browser::Default] is supported, and
/// in options, only [BrowserOptions::dry_run] is honoured.
#[inline]
pub fn open_browser_internal(browser: Browser, url: &str, options: &BrowserOptions) -> Result<()> {
    match browser {
        Browser::Default => open_browser_default(url, options),
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "only default browser supported",
        )),
    }
}

/// Open the default browser
#[inline]
pub fn open_browser_default(url: &str, options: &BrowserOptions) -> Result<()> {
    // always return true for a dry run
    if options.dry_run {
        return Ok(());
    }

    // Create a VM for executing Java calls
    let ctx = ndk_context::android_context();
    let vm = unsafe {
        jni::JavaVM::from_raw(ctx.vm() as _).expect("Expected to find JVM via ndk_context crate")
    };
    let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
    let env = vm.attach_current_thread().map_err(|_| -> Error {
        Error::new(ErrorKind::Other, "Failed to attach current thread")
    })?;

    // Create ACTION_VIEW object
    let intent_class = env
        .find_class("android/content/Intent")
        .map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "Failed to find Intent class") })?;
    let action_view = env
        .get_static_field(intent_class, "ACTION_VIEW", "Ljava/lang/String;")
        .map_err(|_| -> Error {
            Error::new(ErrorKind::NotFound, "Failed to get intent.ACTION_VIEW")
        })?;

    // Create Uri object
    let uri_class = env
        .find_class("android/net/Uri")
        .map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "Failed to find Uri class") })?;
    let url = env
        .new_string(url)
        .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to create JNI string") })?;
    let uri = env
        .call_static_method(
            uri_class,
            "parse",
            "(Ljava/lang/String;)Landroid/net/Uri;",
            &[JValue::Object(*url)],
        )
        .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to parse JNI Uri") })?;

    // Create new ACTION_VIEW intent with the uri
    let intent = env
        .alloc_object(intent_class)
        .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to allocate intent") })?;
    env.call_method(
        intent,
        "<init>",
        "(Ljava/lang/String;Landroid/net/Uri;)V",
        &[action_view, uri],
    )
    .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to initialize intent") })?;

    // Start the intent activity.
    env.call_method(
        activity,
        "startActivity",
        "(Landroid/content/Intent;)V",
        &[JValue::Object(intent)],
    )
    .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to start activity") })?;

    Ok(())
}
<commit_msg>android: address failure on termux in absence of vm context<commit_after>use crate::{Browser, BrowserOptions, Error, ErrorKind, Result};
use jni::objects::JValue;
use std::process::{Command, Stdio};

/// Deal with opening of browsers on Android. Only [Browser::Default] is supported, and
/// in options, only [BrowserOptions::dry_run] is honoured.
#[inline]
pub fn open_browser_internal(browser: Browser, url: &str, options: &BrowserOptions) -> Result<()> {
    match browser {
        Browser::Default => open_browser_default(url, options),
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "only default browser supported",
        )),
    }
}

/// Open the default browser
#[inline]
fn open_browser_default(url: &str, options: &BrowserOptions) -> Result<()> {
    // always return true for a dry run
    if options.dry_run {
        return Ok(());
    }

    // Create a VM for executing Java calls
    let ctx = ndk_context::android_context();
    let vm = match unsafe { jni::JavaVM::from_raw(ctx.vm() as _) } {
        Ok(x) => x,
        Err(_) => {
            // if we didn't get the vm instance, maybe we're running
            // inside a termux, so try with that
            return try_for_termux(url, options).map_err(|_| -> Error {
                Error::new(
                    ErrorKind::NotFound,
                    "Expected to find JVM via ndk_context crate",
                )
            });
        }
    };

    let activity = unsafe { jni::objects::JObject::from_raw(ctx.context() as _) };
    let env = vm.attach_current_thread().map_err(|_| -> Error {
        Error::new(ErrorKind::Other, "Failed to attach current thread")
    })?;

    // Create ACTION_VIEW object
    let intent_class = env
        .find_class("android/content/Intent")
        .map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "Failed to find Intent class") })?;
    let action_view = env
        .get_static_field(intent_class, "ACTION_VIEW", "Ljava/lang/String;")
        .map_err(|_| -> Error {
            Error::new(ErrorKind::NotFound, "Failed to get intent.ACTION_VIEW")
        })?;

    // Create Uri object
    let uri_class = env
        .find_class("android/net/Uri")
        .map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "Failed to find Uri class") })?;
    let url = env
        .new_string(url)
        .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to create JNI string") })?;
    let uri = env
        .call_static_method(
            uri_class,
            "parse",
            "(Ljava/lang/String;)Landroid/net/Uri;",
            &[JValue::Object(*url)],
        )
        .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to parse JNI Uri") })?;

    // Create new ACTION_VIEW intent with the uri
    let intent = env
        .alloc_object(intent_class)
        .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to allocate intent") })?;
    env.call_method(
        intent,
        "<init>",
        "(Ljava/lang/String;Landroid/net/Uri;)V",
        &[action_view, uri],
    )
    .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to initialize intent") })?;

    // Start the intent activity.
    env.call_method(
        activity,
        "startActivity",
        "(Landroid/content/Intent;)V",
        &[JValue::Object(intent)],
    )
    .map_err(|_| -> Error { Error::new(ErrorKind::Other, "Failed to start activity") })?;

    Ok(())
}

/// Attemps to open a browser assuming a termux environment
///
/// See [issue #53](https://github.com/amodm/webbrowser-rs/issues/53)
#[inline]
fn try_for_termux(url: &str, options: &BrowserOptions) -> Result<()> {
    use std::env;
    if env::var("TERMUX_VERSION").is_ok() {
        // return true on dry-run given that termux-open command is guaranteed to be present
        if options.dry_run {
            return Ok(());
        }
        let mut cmd = Command::new("termux-open");
        cmd.arg(url);
        if options.suppress_output {
            cmd.stdin(Stdio::null())
                .stdout(Stdio::null())
                .stderr(Stdio::null());
        }
        cmd.status().and_then(|status| {
            if status.success() {
                Ok(())
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    "command present but exited unsuccessfully",
                ))
            }
        })
    } else {
        Err(Error::new(ErrorKind::Other, "Not a termux environment"))
    }
}
<|endoftext|>


<commit_before>use rustc_serialize::{Encodable, Decodable};
use super::master::{self, Master};
use super::slave::Slave;
use super::error::{ErrorKind, Result};
use super::error::master::Result as MasterResult;
use super::value::Topic;
use super::naming::{self, Resolver};
use super::resolve;
use tcpros::{Client, Message, PublisherStream, ServicePair, ServiceResult};
use rosxmlrpc::serde::XmlRpcValue;

pub struct Ros {
    master: Master,
    slave: Slave,
    hostname: String,
    resolver: Resolver,
    name: String,
}

impl Ros {
    pub fn new(name: &str) -> Result<Ros> {
        let namespace = resolve::namespace();
        let master_uri = resolve::master();
        let hostname = resolve::hostname();
        let name = resolve::name(name);
        let mut ros = Ros::new_raw(&master_uri, &hostname, &namespace, &name)?;
        for (src, dest) in resolve::mappings() {
            ros.map(&src, &dest)?;
        }
        Ok(ros)
    }

    fn new_raw(master_uri: &str, hostname: &str, namespace: &str, name: &str) -> Result<Ros> {
        let namespace = namespace.trim_right_matches("/");

        if name.contains("/") {
            bail!(ErrorKind::Naming(naming::error::ErrorKind::IllegalCharacter(name.into())));
        }

        let name = format!("{}/{}", namespace, name);
        let resolver = Resolver::new(&name)?;

        let slave = Slave::new(&master_uri, &hostname, 0, &name)?;
        let master = Master::new(&master_uri, &name, &slave.uri());
        Ok(Ros {
               master: master,
               slave: slave,
               hostname: String::from(hostname),
               resolver: resolver,
               name: name,
           })
    }

    fn map(&mut self, source: &str, destination: &str) -> Result<()> {
        self.resolver.map(source, destination).map_err(|v| v.into())
    }

    pub fn uri(&self) -> &str {
        return self.slave.uri();
    }

    pub fn param<'a, 'b>(&'a self, name: &'b str) -> Option<Parameter<'a>> {
        self.resolver
            .translate(name)
            .ok()
            .map(|v| {
                     Parameter {
                         master: &self.master,
                         name: v,
                     }
                 })
    }

    pub fn parameters(&self) -> MasterResult<Vec<String>> {
        self.master.get_param_names()
    }

    pub fn state(&self) -> MasterResult<master::SystemState> {
        self.master.get_system_state()
    }

    pub fn topics(&self) -> MasterResult<Vec<Topic>> {
        self.master.get_topic_types()
    }

    pub fn client<T: ServicePair>(&self, service: &str) -> Result<Client<T>> {
        let name = self.resolver.translate(service)?;
        let uri = self.master.lookup_service(&name)?;
        Ok(Client::new(&self.name, &uri, &name))
    }

    pub fn service<T, F>(&mut self, service: &str, handler: F) -> Result<()>
        where T: ServicePair,
              F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static
    {
        let name = self.resolver.translate(service)?;
        let api = self.slave
            .add_service::<T, F>(&self.hostname, &name, handler)?;

        if let Err(err) = self.master.register_service(&name, &api) {
            self.slave.remove_service(&name);
            self.master.unregister_service(&name, &api)?;
            Err(err.into())
        } else {
            Ok(())
        }

    }

    pub fn subscribe<T, F>(&mut self, topic: &str, callback: F) -> Result<()>
        where T: Message,
              F: Fn(T) -> () + Send + 'static
    {
        let name = self.resolver.translate(topic)?;
        self.slave.add_subscription::<T, F>(&name, callback)?;

        match self.master.register_subscriber(&name, &T::msg_type()) {
            Ok(publishers) => {
                if let Err(err) =
                    self.slave
                        .add_publishers_to_subscription(&name, publishers.into_iter()) {
                    error!("Failed to subscribe to all publishers of topic '{}': {}",
                           name,
                           err);
                }
                Ok(())
            }
            Err(err) => {
                self.slave.remove_subscription(&name);
                self.master.unregister_subscriber(&name)?;
                Err(err.into())
            }
        }
    }

    pub fn publish<T>(&mut self, topic: &str) -> Result<PublisherStream<T>>
        where T: Message
    {
        let name = self.resolver.translate(topic)?;
        let stream = self.slave.add_publication::<T>(&self.hostname, &name)?;
        match self.master.register_publisher(&name, &T::msg_type()) {
            Ok(_) => Ok(stream),
            Err(error) => {
                error!("Failed to register publisher for topic '{}': {}",
                       name,
                       error);
                self.slave.remove_publication(&name);
                self.master.unregister_publisher(&name)?;
                Err(error.into())
            }
        }
    }
}

pub struct Parameter<'a> {
    master: &'a Master,
    name: String,
}

impl<'a> Parameter<'a> {
    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn get<T: Decodable>(&self) -> MasterResult<T> {
        self.master.get_param::<T>(&self.name)
    }

    pub fn get_raw(&self) -> MasterResult<XmlRpcValue> {
        self.master.get_param_any(&self.name)
    }

    pub fn set<T: Encodable>(&self, value: &T) -> MasterResult<()> {
        self.master.set_param::<T>(&self.name, value).and(Ok(()))
    }

    pub fn delete(&self) -> MasterResult<()> {
        self.master.delete_param(&self.name).and(Ok(()))
    }

    pub fn exists(&self) -> MasterResult<bool> {
        self.master.has_param(&self.name)
    }

    pub fn search(&self) -> MasterResult<String> {
        self.master.search_param(&self.name)
    }
}
<commit_msg>implement wait_for_service()<commit_after>use rustc_serialize::{Encodable, Decodable};
use super::master::{self, Master};
use super::slave::Slave;
use super::error::{ErrorKind, Result};
use super::error::master::Result as MasterResult;
use super::value::Topic;
use super::naming::{self, Resolver};
use super::resolve;
use tcpros::{Client, Message, PublisherStream, ServicePair, ServiceResult};
use rosxmlrpc::serde::XmlRpcValue;

pub struct Ros {
    master: Master,
    slave: Slave,
    hostname: String,
    resolver: Resolver,
    name: String,
}

impl Ros {
    pub fn new(name: &str) -> Result<Ros> {
        let namespace = resolve::namespace();
        let master_uri = resolve::master();
        let hostname = resolve::hostname();
        let name = resolve::name(name);
        let mut ros = Ros::new_raw(&master_uri, &hostname, &namespace, &name)?;
        for (src, dest) in resolve::mappings() {
            ros.map(&src, &dest)?;
        }
        Ok(ros)
    }

    fn new_raw(master_uri: &str, hostname: &str, namespace: &str, name: &str) -> Result<Ros> {
        let namespace = namespace.trim_right_matches("/");

        if name.contains("/") {
            bail!(ErrorKind::Naming(naming::error::ErrorKind::IllegalCharacter(name.into())));
        }

        let name = format!("{}/{}", namespace, name);
        let resolver = Resolver::new(&name)?;

        let slave = Slave::new(&master_uri, &hostname, 0, &name)?;
        let master = Master::new(&master_uri, &name, &slave.uri());
        Ok(Ros {
               master: master,
               slave: slave,
               hostname: String::from(hostname),
               resolver: resolver,
               name: name,
           })
    }

    fn map(&mut self, source: &str, destination: &str) -> Result<()> {
        self.resolver.map(source, destination).map_err(|v| v.into())
    }

    pub fn uri(&self) -> &str {
        return self.slave.uri();
    }

    pub fn param<'a, 'b>(&'a self, name: &'b str) -> Option<Parameter<'a>> {
        self.resolver
            .translate(name)
            .ok()
            .map(|v| {
                     Parameter {
                         master: &self.master,
                         name: v,
                     }
                 })
    }

    pub fn parameters(&self) -> MasterResult<Vec<String>> {
        self.master.get_param_names()
    }

    pub fn state(&self) -> MasterResult<master::SystemState> {
        self.master.get_system_state()
    }

    pub fn topics(&self) -> MasterResult<Vec<Topic>> {
        self.master.get_topic_types()
    }

    pub fn client<T: ServicePair>(&self, service: &str) -> Result<Client<T>> {
        let name = self.resolver.translate(service)?;
        let uri = self.master.lookup_service(&name)?;
        Ok(Client::new(&self.name, &uri, &name))
    }

    pub fn wait_for_service(&self, service: &str) -> Result<()> {
        use super::error::master::ErrorKind::Api;
        use super::error::api::ErrorKind::BadData;
        use ::std::thread::sleep;
        use ::std::time::Duration;

        let name = self.resolver.translate(service)?;
        loop {
            match self.master.lookup_service(&name) {
                Err(e) => {
                    match e.kind() {
                        &Api(BadData(ref m)) if m == "no provider" => {
                            sleep(Duration::from_millis(100));
                            continue;
                        },
                        _ => {},
                    }
                    return Err(e.into());
                }
                Ok(_) => {break}
            }
        }
        Ok(())
    }

    pub fn service<T, F>(&mut self, service: &str, handler: F) -> Result<()>
        where T: ServicePair,
              F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static
    {
        let name = self.resolver.translate(service)?;
        let api = self.slave
            .add_service::<T, F>(&self.hostname, &name, handler)?;

        if let Err(err) = self.master.register_service(&name, &api) {
            self.slave.remove_service(&name);
            self.master.unregister_service(&name, &api)?;
            Err(err.into())
        } else {
            Ok(())
        }

    }

    pub fn subscribe<T, F>(&mut self, topic: &str, callback: F) -> Result<()>
        where T: Message,
              F: Fn(T) -> () + Send + 'static
    {
        let name = self.resolver.translate(topic)?;
        self.slave.add_subscription::<T, F>(&name, callback)?;

        match self.master.register_subscriber(&name, &T::msg_type()) {
            Ok(publishers) => {
                if let Err(err) =
                    self.slave
                        .add_publishers_to_subscription(&name, publishers.into_iter()) {
                    error!("Failed to subscribe to all publishers of topic '{}': {}",
                           name,
                           err);
                }
                Ok(())
            }
            Err(err) => {
                self.slave.remove_subscription(&name);
                self.master.unregister_subscriber(&name)?;
                Err(err.into())
            }
        }
    }

    pub fn publish<T>(&mut self, topic: &str) -> Result<PublisherStream<T>>
        where T: Message
    {
        let name = self.resolver.translate(topic)?;
        let stream = self.slave.add_publication::<T>(&self.hostname, &name)?;
        match self.master.register_publisher(&name, &T::msg_type()) {
            Ok(_) => Ok(stream),
            Err(error) => {
                error!("Failed to register publisher for topic '{}': {}",
                       name,
                       error);
                self.slave.remove_publication(&name);
                self.master.unregister_publisher(&name)?;
                Err(error.into())
            }
        }
    }
}

pub struct Parameter<'a> {
    master: &'a Master,
    name: String,
}

impl<'a> Parameter<'a> {
    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn get<T: Decodable>(&self) -> MasterResult<T> {
        self.master.get_param::<T>(&self.name)
    }

    pub fn get_raw(&self) -> MasterResult<XmlRpcValue> {
        self.master.get_param_any(&self.name)
    }

    pub fn set<T: Encodable>(&self, value: &T) -> MasterResult<()> {
        self.master.set_param::<T>(&self.name, value).and(Ok(()))
    }

    pub fn delete(&self) -> MasterResult<()> {
        self.master.delete_param(&self.name).and(Ok(()))
    }

    pub fn exists(&self) -> MasterResult<bool> {
        self.master.has_param(&self.name)
    }

    pub fn search(&self) -> MasterResult<String> {
        self.master.search_param(&self.name)
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::sync::atomic::{spin_loop_hint as cpu_relax};

use crate::Mutex;

/// A barrier enables multiple threads to synchronize the beginning
/// of some computation.
///
/// # Examples
///
/// ```
/// use spin;
/// use std::sync::Arc;
/// use std::thread;
///
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(spin::Barrier::new(10));
/// for _ in 0..10 {
///     let c = barrier.clone();
///     // The same messages will be printed together.
///     // You will NOT see any interleaving.
///     handles.push(thread::spawn(move|| {
///         println!("before wait");
///         c.wait();
///         println!("after wait");
///     }));
/// }
/// // Wait for other threads to finish.
/// for handle in handles {
///     handle.join().unwrap();
/// }
/// ```
pub struct Barrier {
    lock: Mutex<BarrierState>,
    num_threads: usize,
}

// The inner state of a double barrier
struct BarrierState {
    count: usize,
    generation_id: usize,
}

/// A `BarrierWaitResult` is returned by [`wait`] when all threads in the [`Barrier`]
/// have rendezvoused.
///
/// [`wait`]: struct.Barrier.html#method.wait
/// [`Barrier`]: struct.Barrier.html
///
/// # Examples
///
/// ```
/// use spin;
///
/// let barrier = spin::Barrier::new(1);
/// let barrier_wait_result = barrier.wait();
/// ```
pub struct BarrierWaitResult(bool);

impl Barrier {
    /// Creates a new barrier that can block a given number of threads.
    ///
    /// A barrier will block `n`-1 threads which call [`wait`] and then wake up
    /// all threads at once when the `n`th thread calls [`wait`].
    ///
    /// [`wait`]: #method.wait
    ///
    /// # Examples
    ///
    /// ```
    /// use spin;
    ///
    /// let barrier = spin::Barrier::new(10);
    /// ```
    pub const fn new(n: usize) -> Barrier {
        Barrier {
            lock: Mutex::new(BarrierState {
                count: 0,
                generation_id: 0,
            }),
            num_threads: n,
        }
    }

    /// Blocks the current thread until all threads have rendezvoused here.
    ///
    /// Barriers are re-usable after all threads have rendezvoused once, and can
    /// be used continuously.
    ///
    /// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
    /// returns `true` from [`is_leader`] when returning from this function, and
    /// all other threads will receive a result that will return `false` from
    /// [`is_leader`].
    ///
    /// [`BarrierWaitResult`]: struct.BarrierWaitResult.html
    /// [`is_leader`]: struct.BarrierWaitResult.html#method.is_leader
    ///
    /// # Examples
    ///
    /// ```
    /// use spin;
    /// use std::sync::Arc;
    /// use std::thread;
    ///
    /// let mut handles = Vec::with_capacity(10);
    /// let barrier = Arc::new(spin::Barrier::new(10));
    /// for _ in 0..10 {
    ///     let c = barrier.clone();
    ///     // The same messages will be printed together.
    ///     // You will NOT see any interleaving.
    ///     handles.push(thread::spawn(move|| {
    ///         println!("before wait");
    ///         c.wait();
    ///         println!("after wait");
    ///     }));
    /// }
    /// // Wait for other threads to finish.
    /// for handle in handles {
    ///     handle.join().unwrap();
    /// }
    /// ```
    pub fn wait(&self) -> BarrierWaitResult {
        let mut lock = self.lock.lock();
        lock.count += 1;

        if lock.count < self.num_threads {
            // not the leader
            let local_gen = lock.generation_id;

            while local_gen == lock.generation_id &&
                lock.count < self.num_threads {
                drop(lock);
                cpu_relax();
                lock = self.lock.lock();
            }
            return BarrierWaitResult(false);
        } else {
            // this thread is the leader,
            //   and is responsible for incrementing the generation
            lock.count = 0;
            lock.generation_id = lock.generation_id.wrapping_add(1);
            return BarrierWaitResult(true);
        }
    }
}

impl BarrierWaitResult {
    /// Returns whether this thread from [`wait`] is the "leader thread".
    ///
    /// Only one thread will have `true` returned from their result, all other
    /// threads will have `false` returned.
    ///
    /// [`wait`]: struct.Barrier.html#method.wait
    ///
    /// # Examples
    ///
    /// ```
    /// use spin;
    ///
    /// let barrier = spin::Barrier::new(1);
    /// let barrier_wait_result = barrier.wait();
    /// println!("{:?}", barrier_wait_result.is_leader());
    /// ```
    pub fn is_leader(&self) -> bool { self.0 }
}

#[cfg(test)]
mod tests {
    use std::prelude::v1::*;

    use std::sync::mpsc::{channel, TryRecvError};
    use std::sync::Arc;
    use std::thread;

    use super::Barrier;

    fn use_barrier(n: usize, barrier: Arc<Barrier>) {
        let (tx, rx) = channel();

        for _ in 0..n - 1 {
            let c = barrier.clone();
            let tx = tx.clone();
            thread::spawn(move|| {
                tx.send(c.wait().is_leader()).unwrap();
            });
        }

        // At this point, all spawned threads should be blocked,
        // so we shouldn't get anything from the port
        assert!(match rx.try_recv() {
            Err(TryRecvError::Empty) => true,
            _ => false,
        });

        let mut leader_found = barrier.wait().is_leader();

        // Now, the barrier is cleared and we should get data.
        for _ in 0..n - 1 {
            if rx.recv().unwrap() {
                assert!(!leader_found);
                leader_found = true;
            }
        }
        assert!(leader_found);
    }

    #[test]
    fn test_barrier() {
        const N: usize = 10;

        let barrier = Arc::new(Barrier::new(N));

        use_barrier(N, barrier.clone());

        // use barrier twice to ensure it is reusable
        use_barrier(N, barrier.clone());
    }
}
<commit_msg>review feedback<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::sync::atomic::{spin_loop_hint as cpu_relax};

use crate::Mutex;

/// A primitive that synchronizes the execution of multiple threads.
///
/// # Example
///
/// ```
/// use spin;
/// use std::sync::Arc;
/// use std::thread;
///
/// let mut handles = Vec::with_capacity(10);
/// let barrier = Arc::new(spin::Barrier::new(10));
/// for _ in 0..10 {
///     let c = barrier.clone();
///     // The same messages will be printed together.
///     // You will NOT see any interleaving.
///     handles.push(thread::spawn(move|| {
///         println!("before wait");
///         c.wait();
///         println!("after wait");
///     }));
/// }
/// // Wait for other threads to finish.
/// for handle in handles {
///     handle.join().unwrap();
/// }
/// ```
pub struct Barrier {
    lock: Mutex<BarrierState>,
    num_threads: usize,
}

// The inner state of a double barrier
struct BarrierState {
    count: usize,
    generation_id: usize,
}

/// A `BarrierWaitResult` is returned by [`wait`] when all threads in the [`Barrier`]
/// have rendezvoused.
///
/// [`wait`]: struct.Barrier.html#method.wait
/// [`Barrier`]: struct.Barrier.html
///
/// # Examples
///
/// ```
/// use spin;
///
/// let barrier = spin::Barrier::new(1);
/// let barrier_wait_result = barrier.wait();
/// ```
pub struct BarrierWaitResult(bool);

impl Barrier {
    /// Creates a new barrier that can block a given number of threads.
    ///
    /// A barrier will block `n`-1 threads which call [`wait`] and then wake up
    /// all threads at once when the `n`th thread calls [`wait`]. A Barrier created
    /// with n = 0 will behave identically to one created with n = 1.
    ///
    /// [`wait`]: #method.wait
    ///
    /// # Examples
    ///
    /// ```
    /// use spin;
    ///
    /// let barrier = spin::Barrier::new(10);
    /// ```
    pub const fn new(n: usize) -> Barrier {
        Barrier {
            lock: Mutex::new(BarrierState {
                count: 0,
                generation_id: 0,
            }),
            num_threads: n,
        }
    }

    /// Blocks the current thread until all threads have rendezvoused here.
    ///
    /// Barriers are re-usable after all threads have rendezvoused once, and can
    /// be used continuously.
    ///
    /// A single (arbitrary) thread will receive a [`BarrierWaitResult`] that
    /// returns `true` from [`is_leader`] when returning from this function, and
    /// all other threads will receive a result that will return `false` from
    /// [`is_leader`].
    ///
    /// [`BarrierWaitResult`]: struct.BarrierWaitResult.html
    /// [`is_leader`]: struct.BarrierWaitResult.html#method.is_leader
    ///
    /// # Examples
    ///
    /// ```
    /// use spin;
    /// use std::sync::Arc;
    /// use std::thread;
    ///
    /// let mut handles = Vec::with_capacity(10);
    /// let barrier = Arc::new(spin::Barrier::new(10));
    /// for _ in 0..10 {
    ///     let c = barrier.clone();
    ///     // The same messages will be printed together.
    ///     // You will NOT see any interleaving.
    ///     handles.push(thread::spawn(move|| {
    ///         println!("before wait");
    ///         c.wait();
    ///         println!("after wait");
    ///     }));
    /// }
    /// // Wait for other threads to finish.
    /// for handle in handles {
    ///     handle.join().unwrap();
    /// }
    /// ```
    pub fn wait(&self) -> BarrierWaitResult {
        let mut lock = self.lock.lock();
        lock.count += 1;

        if lock.count < self.num_threads {
            // not the leader
            let local_gen = lock.generation_id;

            while local_gen == lock.generation_id &&
                lock.count < self.num_threads {
                drop(lock);
                cpu_relax();
                lock = self.lock.lock();
            }
            return BarrierWaitResult(false);
        } else {
            // this thread is the leader,
            //   and is responsible for incrementing the generation
            lock.count = 0;
            lock.generation_id = lock.generation_id.wrapping_add(1);
            return BarrierWaitResult(true);
        }
    }
}

impl BarrierWaitResult {
    /// Returns whether this thread from [`wait`] is the "leader thread".
    ///
    /// Only one thread will have `true` returned from their result, all other
    /// threads will have `false` returned.
    ///
    /// [`wait`]: struct.Barrier.html#method.wait
    ///
    /// # Examples
    ///
    /// ```
    /// use spin;
    ///
    /// let barrier = spin::Barrier::new(1);
    /// let barrier_wait_result = barrier.wait();
    /// println!("{:?}", barrier_wait_result.is_leader());
    /// ```
    pub fn is_leader(&self) -> bool { self.0 }
}

#[cfg(test)]
mod tests {
    use std::prelude::v1::*;

    use std::sync::mpsc::{channel, TryRecvError};
    use std::sync::Arc;
    use std::thread;

    use super::Barrier;

    fn use_barrier(n: usize, barrier: Arc<Barrier>) {
        let (tx, rx) = channel();

        for _ in 0..n - 1 {
            let c = barrier.clone();
            let tx = tx.clone();
            thread::spawn(move|| {
                tx.send(c.wait().is_leader()).unwrap();
            });
        }

        // At this point, all spawned threads should be blocked,
        // so we shouldn't get anything from the port
        assert!(match rx.try_recv() {
            Err(TryRecvError::Empty) => true,
            _ => false,
        });

        let mut leader_found = barrier.wait().is_leader();

        // Now, the barrier is cleared and we should get data.
        for _ in 0..n - 1 {
            if rx.recv().unwrap() {
                assert!(!leader_found);
                leader_found = true;
            }
        }
        assert!(leader_found);
    }

    #[test]
    fn test_barrier() {
        const N: usize = 10;

        let barrier = Arc::new(Barrier::new(N));

        use_barrier(N, barrier.clone());

        // use barrier twice to ensure it is reusable
        use_barrier(N, barrier.clone());
    }
}
<|endoftext|>


<commit_before>extern crate erl_ext;

use erl_ext::{Decoder,Encoder};
use std::io;
use std::os;

fn main() {
    let args = os::args();
    if args.len() < 3 {
        // TODO: make encoder opts configurable
        println!("Usage: codec <in-file or '-'> <out-file or '-'>");
        os::set_exit_status(1);
        return
    }
    let mut in_f = match args[1].as_slice() {
        "-" => box io::stdin() as Box<io::Reader>,
        other =>
            box io::File::open(&Path::new(other)).unwrap() as Box<io::Reader>
    };
    let mut out_f = match args[2].as_slice() {
        "-" => box io::stdout() as Box<io::Writer>,
        other =>
            box io::File::create(&Path::new(other)).unwrap() as Box<io::Writer>
    };

    let src = in_f.read_to_end().unwrap();

    let mut rdr = io::MemReader::new(src);
    let mut wrtr = io::MemWriter::new();
    {
        // decode term
        let mut decoder = Decoder::new(&mut rdr);
        match decoder.read_prelude() {
            Ok(false) =>
                fail!("Invalid eterm!"),
            Err(io::IoError{desc: d, ..}) =>
                fail!("IoError: {}", d),
            _ => ()
        }
        let term = decoder.decode_term().unwrap();
        // print it to stderr
        (write!(io::stderr(), "{}", term)).unwrap();
        // and encode it
        let mut encoder = Encoder::new(&mut wrtr, false, false, true);
        encoder.write_prelude().unwrap();
        encoder.encode_term(term).unwrap();
    }
    // write encoded result to out_f
    out_f.write(wrtr.get_ref()).unwrap();

    // compare original and encoded
    if wrtr.get_ref() != rdr.get_ref() {
        (write!(io::stderr(), "Before and After isn't equal")).unwrap();
        os::set_exit_status(1);
        return
    }
}
<commit_msg>Improeved 'codec' example (configurable encoder options)<commit_after>extern crate erl_ext;
extern crate getopts;

use getopts::{optopt,optflag,getopts,OptGroup};
use erl_ext::{Decoder,Encoder};
use std::io;
use std::os;

fn main() {
    let args = os::args();
    let opts = [
        optflag("u", "utf8-atoms", "Use utf-8 atoms feature"),
        optflag("s", "small-atoms", "Use small atoms feature"),
        optflag("f", "fair-new-fun", "Fairly calculate NEW_FUN size (requires extra memory)"),
        ];
    let matches = match getopts(args.tail(), opts) {
        Ok(m) => { m }
        Err(f) => { fail!(f.to_string()) }
    };
    if matches.free.len() != 2 {
        println!("Usage: {} [opts] <in-file or '-'> <out-file or '-'>", args[0]);
        for o in opts.iter() {
            println!("-{}\t--{}\t{}", o.short_name, o.long_name, o.desc);
        }
        os::set_exit_status(1);
        return
    }
    let mut in_f = match matches.free[0].as_slice() {
        "-" => box io::stdin() as Box<io::Reader>,
        other =>
            box io::File::open(&Path::new(other)).unwrap() as Box<io::Reader>
    };
    let mut out_f = match matches.free[1].as_slice() {
        "-" => box io::stdout() as Box<io::Writer>,
        other =>
            box io::File::create(&Path::new(other)).unwrap() as Box<io::Writer>
    };

    let src = in_f.read_to_end().unwrap();

    let mut rdr = io::MemReader::new(src);
    let mut wrtr = io::MemWriter::new();
    {
        // decode term
        let mut decoder = Decoder::new(&mut rdr);
        match decoder.read_prelude() {
            Ok(false) =>
                fail!("Invalid eterm!"),
            Err(io::IoError{desc: d, ..}) =>
                fail!("IoError: {}", d),
            _ => ()
        }
        let term = decoder.decode_term().unwrap();
        // print it to stderr
        (write!(io::stderr(), "{}\n", term)).unwrap();
        // and encode it
        let mut encoder = Encoder::new(&mut wrtr,
                                       matches.opt_present("u"),
                                       matches.opt_present("s"),
                                       matches.opt_present("f"));
        encoder.write_prelude().unwrap();
        encoder.encode_term(term).unwrap();
    }
    // write encoded result to out_f
    out_f.write(wrtr.get_ref()).unwrap();

    // compare original and encoded
    if wrtr.get_ref() != rdr.get_ref() {
        (write!(io::stderr(), "Before and After isn't equal\n")).unwrap();
        os::set_exit_status(1);
        return
    }
}
<|endoftext|>


<commit_before>/* Cymbalum, Molecular Simulation in Rust - Copyright (C) 2015 Guillaume Fraux
 *
 * 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/
 */

//! Logging configuration
//!
//! This module provides a `Logger` struct which can be used for logging
//! messages from Cymbalum. One and only one of the initilialization function
//! should be called, and it should only be called once. Any call to
//! initializing function after the first one will be ignored.
//!
//! Other [log](https://github.com/rust-lang/log) compatible crates can be used
//! for logging if you whant.

use std::io;
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::path::Path;
use std::sync::{Arc, Mutex};

extern crate log;
use log::{Log, LogRecord, LogMetadata, set_logger};
pub use log::LogLevel;

/// Logger with capacity to write to the standard output stream, the standard
/// error stream or a file.
 pub struct Logger {
     level: LogLevel,
     writer: Arc<Mutex<Box<Write + Send + Sync>>>,
 }

 impl Logger{
     fn new<T>(level: LogLevel, handle: T) -> Logger where T: Write + Send + Sync + 'static {
         Logger{
             level: level,
             writer: Arc::new(Mutex::new(Box::new(handle))),
         }
     }

     fn init(logger: Logger) {
         let res = set_logger(|max_log_level| {
             max_log_level.set(logger.level.to_log_level_filter());
             Box::new(logger)
         });

         if let Err(_) = res {
             warn!("Logger was initialized more than once! This call is ignored.");
         }
     }

     /// Initialize the global logger to use the standard output stream.
     pub fn stdout() {
         let logger = Logger::new(levels::DEFAULT_LEVEL, io::stdout());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard output stream, with
     /// the maximum log level of `level`
     pub fn stdout_at_level(level: LogLevel) {
         let logger = Logger::new(level, io::stdout());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard error stream.
     pub fn stderr() {
         let logger = Logger::new(levels::DEFAULT_LEVEL, io::stderr());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard error stream, with
     /// the maximum log level of `level`
     pub fn stderr_at_level(level: LogLevel) {
         let logger = Logger::new(level, io::stderr());
         Logger::init(logger);
     }

     /// Initialize the global logger to write to the file at `path`, creating
     /// the file if needed. If the file already exists, it is not removed.
     pub fn file<P: AsRef<Path>>(path: P) -> Result<(), io::Error> {
         let file = try!(OpenOptions::new().write(true).append(true).open(path));
         let logger = Logger::new(levels::DEFAULT_LEVEL, file);
         Logger::init(logger);
         Ok(())
     }

     /// Initialize the global logger to write to the file at `path`, creating
     /// the file if needed. If the file already exists, it is not removed. The
     /// maximum log level is set to `level`.
     pub fn file_at_level<P: AsRef<Path>>(path: P, level: LogLevel) -> Result<(), io::Error> {
         let file = try!(OpenOptions::new().write(true).append(true).open(path));
         let logger = Logger::new(level, file);
         Logger::init(logger);
         Ok(())
     }
 }

#[cfg(not(debug_assertions))]
 mod levels {
     use super::log::LogLevel;
     /// Default log level
     pub const DEFAULT_LEVEL: LogLevel = LogLevel::Warn;
 }

#[cfg(debug_assertions)]
 mod levels {
     use super::log::LogLevel;
     /// Default log level
     pub const DEFAULT_LEVEL: LogLevel = LogLevel::Debug;
 }

/// Implements Log trait for Logger
impl Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &LogRecord) {
        let mut out = self.writer.lock().ok().expect("Could not lock the logger.");
        let write_res = write!(
            &mut out, "{}: {} at {}:{}\n",
            record.level(), record.args(),
            record.location().file(), record.location().line()
        );

        if let Err(err) = write_res {
            let error_res = write!(& mut io::stderr(),
                "Error while writing log: {:?}\nMessage was: {} at {}:{} ({})\n",
                err, record.args(),
                record.location().file(), record.location().line(),
                record.level()
            );
            if let Err(err2) = error_res {
                panic!(format!(
                    "Error while logging logger error, aborting. Error chain: {:?} -> {:?} from {}:{}",
                    err, err2,
                    record.location().file(), record.location().line(),
                ))
            }
        }
    }
}
<commit_msg>Small logging improvements<commit_after>/* Cymbalum, Molecular Simulation in Rust - Copyright (C) 2015 Guillaume Fraux
 *
 * 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/
 */

//! Logging configuration
//!
//! This module provides a `Logger` struct which can be used for logging
//! messages from Cymbalum. One and only one of the initilialization function
//! should be called, and it should only be called once. Any call to
//! initializing function after the first one will be ignored.
//!
//! Other [log](https://github.com/rust-lang/log) compatible crates can be used
//! for logging if you whant.

use std::io;
use std::io::prelude::*;
use std::fs::OpenOptions;
use std::path::Path;
use std::sync::{Arc, Mutex};

extern crate log;
use log::{Log, LogRecord, LogMetadata, set_logger};
pub use log::LogLevel;

/// Logger with capacity to write to the standard output stream, the standard
/// error stream or a file.
 pub struct Logger {
     level: LogLevel,
     writer: Arc<Mutex<Box<Write + Send + Sync>>>,
 }

 impl Logger{
     fn new<T>(level: LogLevel, handle: T) -> Logger where T: Write + Send + Sync + 'static {
         Logger{
             level: level,
             writer: Arc::new(Mutex::new(Box::new(handle))),
         }
     }

     fn init(logger: Logger) {
         let res = set_logger(|max_log_level| {
             max_log_level.set(logger.level.to_log_level_filter());
             Box::new(logger)
         });

         if let Err(_) = res {
             warn!("Logger was initialized more than once! This call is ignored.");
         }
     }

     /// Initialize the global logger to use the standard output stream.
     pub fn stdout() {
         let logger = Logger::new(levels::DEFAULT_LEVEL, io::stdout());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard output stream, with
     /// the maximum log level of `level`
     pub fn stdout_at_level(level: LogLevel) {
         let logger = Logger::new(level, io::stdout());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard error stream.
     pub fn stderr() {
         let logger = Logger::new(levels::DEFAULT_LEVEL, io::stderr());
         Logger::init(logger);
     }

     /// Initialize the global logger to use the standard error stream, with
     /// the maximum log level of `level`
     pub fn stderr_at_level(level: LogLevel) {
         let logger = Logger::new(level, io::stderr());
         Logger::init(logger);
     }

     /// Initialize the global logger to write to the file at `path`, creating
     /// the file if needed. If the file already exists, it is not removed.
     pub fn file<P: AsRef<Path>>(path: P) -> Result<(), io::Error> {
         let file = try!(OpenOptions::new().write(true).append(true).open(path));
         let logger = Logger::new(levels::DEFAULT_LEVEL, file);
         Logger::init(logger);
         Ok(())
     }

     /// Initialize the global logger to write to the file at `path`, creating
     /// the file if needed. If the file already exists, it is not removed. The
     /// maximum log level is set to `level`.
     pub fn file_at_level<P: AsRef<Path>>(path: P, level: LogLevel) -> Result<(), io::Error> {
         let file = try!(OpenOptions::new().write(true).append(true).open(path));
         let logger = Logger::new(level, file);
         Logger::init(logger);
         Ok(())
     }
 }

#[cfg(not(debug_assertions))]
 mod levels {
     use super::log::LogLevel;
     /// Default log level
     pub const DEFAULT_LEVEL: LogLevel = LogLevel::Warn;
 }

#[cfg(debug_assertions)]
 mod levels {
     use super::log::LogLevel;
     /// Default log level
     pub const DEFAULT_LEVEL: LogLevel = LogLevel::Debug;
 }

/// Implements Log trait for Logger
impl Log for Logger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &LogRecord) {
        let mut out = self.writer.lock().ok().expect("Could not lock the logger.");
        let write_res = match record.level() {
            LogLevel::Info => write!(&mut out, "{}\n", record.args()),
            LogLevel::Warn => write!(
                   &mut out, "Warning: {} from {}:{}\n",
                   record.args(),
                   record.location().file(), record.location().line()
            ),
            _ => write!(
                   &mut out, "{}: {} from {}:{}\n",
                   record.level(), record.args(),
                   record.location().file(), record.location().line()
            ),
        };

        if let Err(err) = write_res {
            let error_res = write!(& mut io::stderr(),
                "Error while writing log: {:?}\nMessage was: {} at {}:{} ({})\n",
                err, record.args(),
                record.location().file(), record.location().line(),
                record.level()
            );
            if let Err(err2) = error_res {
                panic!(format!(
                    "Error while logging logger error, aborting. Error chain: {:?} -> {:?} from {}:{}",
                    err, err2,
                    record.location().file(), record.location().line(),
                ))
            }
        }
    }
}
<|endoftext|>