<commit_before>use entities::*;
use super::geo::Coordinate;

pub trait InBBox {
    fn in_bbox(&self, bb: &[Coordinate]) -> bool;
}

pub enum Combination {
    And,
    Or
}

impl InBBox for Entry {
    fn in_bbox(&self, bb: &[Coordinate]) -> bool {
        // TODO: either return a Result or create a bounding box struct
        if bb.len() != 2 {
            warn!("invalid bounding box: {:?}", bb);
            return false;
        }
        bb.len() == 2 && self.lat >= bb[0].lat && self.lng >= bb[0].lng && self.lat <= bb[1].lat &&
        self.lng <= bb[1].lng
    }
}

pub fn entries_by_category_ids<'a>(ids: &'a [String]) -> Box<Fn(&&Entry) -> bool + 'a> {
    Box::new(move |e| ids.iter().any(|c| e.categories.iter().any(|x| x == c)))
}

pub fn triple_by_subject<'a>(o_id : ObjectId) -> Box<Fn(&&Triple) -> bool + 'a> {
    Box::new(move |triple| o_id == triple.subject)
}

pub fn entries_by_tags<'a>(tags: &'a [String], triples: &'a [Triple], combination: Combination) -> Box<Fn(&&Entry) -> bool + 'a> {

    let triples : Vec<(&String, &String)> = triples
        .into_iter()
        .filter_map(|x| match *x {
            Triple {
                subject   : ObjectId::Entry(ref e_id),
                predicate : Relation::IsTaggedWith,
                object    : ObjectId::Tag(ref t_id)
            } => Some((e_id,t_id)),
            _ => None
        })
        .collect();

    match combination {
        Combination::Or => {
            Box::new(move |entry|
                tags.iter()
                    .any(|tag| triples.iter().any(|t| *t.0 == entry.id && t.1 == tag))
            )
        },
        Combination::And => {
            Box::new(move |entry| {
                let e_tags : Vec<&String> = triples
                    .iter()
                    .filter(|t| *t.0 == entry.id)
                    .map(|t|t.1)
                    .collect();
                    tags.iter().all(|tag|e_tags.iter().any(|t| *t == tag))
                }
            )
        }
    }
}

pub fn entries_by_search_text<'a>(text: &'a str) -> Box<Fn(&&Entry) -> bool + 'a> {
    let words = to_words(text);
    Box::new(move |e| {
        words.iter().any(|word| {
            e.title.to_lowercase().contains(word) || e.description.to_lowercase().contains(word)
        })
    })
}

pub fn entries_by_tags_or_search_text<'a>(text: &'a str, tags: &'a [String], triples: &'a [Triple]) -> Box<Fn(&&Entry) -> bool + 'a> {
    let tag_triples : Vec<(&String, &String)> = triples
        .into_iter()
        .filter_map(|x| match *x {
            Triple {
                subject   : ObjectId::Entry(ref e_id),
                predicate : Relation::IsTaggedWith,
                object    : ObjectId::Tag(ref t_id)
            } => Some((e_id,t_id)),
            _ => None
        })
        .collect();

    let words = to_words(text);

    Box::new(move |entry|
        tags.iter()
            .any(|tag| tag_triples.iter().any(|t| *t.0 == entry.id && t.1 == tag))
        || words.iter().any(|word| {
            entry.title.to_lowercase().contains(word) || entry.description.to_lowercase().contains(word)
        })
    )
}

fn to_words(txt: &str) -> Vec<String> {
    txt.to_lowercase().split(',').map(|x| x.to_string()).collect()
}

#[cfg(test)]
mod tests {

    use super::*;
    use business::builder::*;

    #[test]
    fn is_in_bounding_box() {
        let bb = vec![Coordinate {
                          lat: -10.0,
                          lng: -10.0,
                      },
                      Coordinate {
                          lat: 10.0,
                          lng: 10.0,
                      }];
        let e = Entry::build()
            .title("foo")
            .description("bar")
            .lat(5.0)
            .lng(5.0)
            .finish();
        assert_eq!(e.in_bbox(&bb), true);
        let e = Entry::build()
            .title("foo")
            .description("bar")
            .lat(10.1)
            .lng(10.0)
            .finish();
        assert_eq!(e.in_bbox(&bb), false);
    }

    #[test]
    fn is_in_invalid_bounding_box() {
        let bb = vec![Coordinate {
                          lat: 10.0,
                          lng: 10.0,
                      }];
        let e = Entry::build()
            .lat(5.0)
            .lng(5.0)
            .finish();
        assert_eq!(e.in_bbox(&bb), false);
    }

    #[test]
    fn filter_by_bounding_box() {
        let bb = vec![Coordinate {
                          lat: -10.0,
                          lng: -10.0,
                      },
                      Coordinate {
                          lat: 10.0,
                          lng: 10.0,
                      }];
        let entries = vec![
            Entry::build().lat(5.0).lng(5.0).finish(),
            Entry::build().lat(-5.0).lng(5.0).finish(),
            Entry::build().lat(10.0).lng(10.1).finish(),
        ];
        assert_eq!(entries.iter().filter(|&x| x.in_bbox(&bb)).collect::<Vec<&Entry>>().len(),
                   2);
    }

    #[test]
    fn filter_by_category() {
        let entries = vec![
            Entry::build().categories(vec!["a"]).finish(),
            Entry::build().categories(vec!["c"]).finish(),
            Entry::build().categories(vec!["b","a"]).finish(),
        ];
        let ab = vec!["a".into(), "b".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_category_ids(&ab)).collect();
        assert_eq!(x.len(), 2);
        let b = vec!["b".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_category_ids(&b)).collect();
        assert_eq!(x.len(), 1);
        let c = vec!["c".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_category_ids(&c)).collect();
        assert_eq!(x.len(), 1);
    }

    #[test]
    fn search_entries_by_title() {
        let entries = vec![
            Entry::build().title("a title").description("x").finish(),
            Entry::build().title("not so interesting").description("y").finish(),
        ];
        let filter = entries_by_search_text("a");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].title, "a title");
        let filter = entries_by_search_text("ti");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 2);
    }

    #[test]
    fn search_entries_by_description() {
        let entries = vec![
            Entry::build().title("a").description("x").finish(),
            Entry::build().title("b").description("y").finish(),
            Entry::build().title("c").description("x").finish(),
        ];
        let filter = entries_by_search_text("x");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 2);
    }

    #[test]
    fn search_with_multiple_words() {
        let entries = vec![
            Entry::build().title("SoLaWi").description("mit gemüse").finish(),
            Entry::build().title("csa").description("This is a great csa").finish(),
            Entry::build().title("solawi").description("Das ist eine tolle solawi").finish(),
        ];
        let filter = entries_by_search_text("csa,toll");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 2);
        let filter = entries_by_search_text("great,to,gemü");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 3);
    }

    #[test]
    fn search_and_ignore_capitalisation() {
        let e0 = Entry::build().title("Eintrag").description("Hallo! Ein EinTrag").finish();
        let e1 = Entry::build().title("Ein trag").description("foo").finish();
        let e2 = Entry::build().title("CSA").description("cool vegetables").finish();
        let entries = vec![&e0, &e1, &e2];

        let filter = entries_by_search_text("Foo");
        let x: Vec<&Entry> = entries.iter().cloned().filter(&*filter).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].id, e1.id);

        let filter = entries_by_search_text("trag");
        let x: Vec<&Entry> = entries.iter().cloned().filter(&*filter).collect();
        assert_eq!(x.len(), 2);
        assert_eq!(x[0].id, e0.id);
        assert_eq!(x[1].id, e1.id);

        let filter = entries_by_search_text("csa");
        let x: Vec<&Entry> = entries.iter().cloned().filter(&*filter).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].id, e2.id);
    }

    #[test]
    fn filter_by_tags() {
        let entries = vec![
            Entry::build().id("a").finish(),
            Entry::build().id("b").finish(),
            Entry::build().id("c").finish(),
        ];
        let tags = vec!["csa".into()];
        let no_triples = vec![];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags(&tags,&no_triples, Combination::Or)).collect();
        assert_eq!(x.len(), 0);
        let triples = vec![
            Triple{ subject: ObjectId::Entry("b".into()), predicate: Relation::IsTaggedWith, object: ObjectId::Tag("csa".into())},
            Triple{ subject: ObjectId::Entry("c".into()), predicate: Relation::IsTaggedWith, object: ObjectId::Tag("foo".into())}
        ];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags(&tags,&triples, Combination::Or)).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].id,"b");
        let tags = vec!["csa".into(),"foo".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags(&tags,&triples, Combination::Or)).collect();
        assert_eq!(x.len(), 2);
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags(&tags,&triples, Combination::And)).collect();
        assert_eq!(x.len(), 0);
    }

    #[test]
    fn filter_by_tags_or_text() {
        let entries = vec![
            Entry::build().id("a").title("solawi").finish(),
            Entry::build().id("b").title("blabla").description("blubb").finish(),
            Entry::build().id("c").finish(),
        ];
        let tags = vec!["csa".into()];
        let solawi = "solawi";
        let blubb = "blubb";
        let slowtec = "slowtec";
        let no_triples = vec![];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags_or_search_text(&slowtec, &tags,&no_triples)).collect();
        assert_eq!(x.len(), 0);

        let triples = vec![
            Triple{ subject: ObjectId::Entry("b".into()), predicate: Relation::IsTaggedWith, object: ObjectId::Tag("csa".into())},
            Triple{ subject: ObjectId::Entry("c".into()), predicate: Relation::IsTaggedWith, object: ObjectId::Tag("foo".into())}
        ];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags_or_search_text(&slowtec, &tags,&triples)).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].id,"b");
        let tags = vec!["csa".into(),"foo".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags_or_search_text(&slowtec, &tags,&triples)).collect();
        assert_eq!(x.len(), 2);
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags_or_search_text(&slowtec, &tags,&triples)).collect();
        assert_eq!(x.len(), 0);

        let triples = vec![
            Triple{ subject: ObjectId::Entry("b".into()), predicate: Relation::IsTaggedWith, object: ObjectId::Tag("csa".into())},
            Triple{ subject: ObjectId::Entry("c".into()), predicate: Relation::IsTaggedWith, object: ObjectId::Tag("foo".into())}
        ];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags_or_search_text(&slowtec, &tags,&triples)).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].id,"b");
        let tags = vec!["csa".into(),"foo".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags_or_search_text(&slowtec, &tags,&triples)).collect();
        assert_eq!(x.len(), 2);
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags_or_search_text(&slowtec, &tags,&triples)).collect();
        assert_eq!(x.len(), 0);
    }
}
<commit_msg>revert 'started to change search to search key or tag'<commit_after>use entities::*;
use super::geo::Coordinate;

pub trait InBBox {
    fn in_bbox(&self, bb: &[Coordinate]) -> bool;
}

pub enum Combination {
    And,
    Or
}

impl InBBox for Entry {
    fn in_bbox(&self, bb: &[Coordinate]) -> bool {
        // TODO: either return a Result or create a bounding box struct
        if bb.len() != 2 {
            warn!("invalid bounding box: {:?}", bb);
            return false;
        }
        bb.len() == 2 && self.lat >= bb[0].lat && self.lng >= bb[0].lng && self.lat <= bb[1].lat &&
        self.lng <= bb[1].lng
    }
}

pub fn entries_by_category_ids<'a>(ids: &'a [String]) -> Box<Fn(&&Entry) -> bool + 'a> {
    Box::new(move |e| ids.iter().any(|c| e.categories.iter().any(|x| x == c)))
}

pub fn triple_by_subject<'a>(o_id : ObjectId) -> Box<Fn(&&Triple) -> bool + 'a> {
    Box::new(move |triple| o_id == triple.subject)
}

pub fn entries_by_tags<'a>(tags: &'a [String], triples: &'a [Triple], combination: Combination) -> Box<Fn(&&Entry) -> bool + 'a> {

    let triples : Vec<(&String, &String)> = triples
        .into_iter()
        .filter_map(|x| match *x {
            Triple {
                subject   : ObjectId::Entry(ref e_id),
                predicate : Relation::IsTaggedWith,
                object    : ObjectId::Tag(ref t_id)
            } => Some((e_id,t_id)),
            _ => None
        })
        .collect();

    match combination {
        Combination::Or => {
            Box::new(move |entry|
                tags.iter()
                    .any(|tag| triples.iter().any(|t| *t.0 == entry.id && t.1 == tag))
            )
        },
        Combination::And => {
            Box::new(move |entry| {
                let e_tags : Vec<&String> = triples
                    .iter()
                    .filter(|t| *t.0 == entry.id)
                    .map(|t|t.1)
                    .collect();
                    tags.iter().all(|tag|e_tags.iter().any(|t| *t == tag))
                }
            )
        }
    }
}

pub fn entries_by_search_text<'a>(text: &'a str) -> Box<Fn(&&Entry) -> bool + 'a> {
    let words = to_words(text);
    Box::new(move |e| {
        words.iter().any(|word| {
            e.title.to_lowercase().contains(word) || e.description.to_lowercase().contains(word)
        })
    })
}

fn to_words(txt: &str) -> Vec<String> {
    txt.to_lowercase().split(',').map(|x| x.to_string()).collect()
}

#[cfg(test)]
mod tests {

    use super::*;
    use business::builder::*;

    #[test]
    fn is_in_bounding_box() {
        let bb = vec![Coordinate {
                          lat: -10.0,
                          lng: -10.0,
                      },
                      Coordinate {
                          lat: 10.0,
                          lng: 10.0,
                      }];
        let e = Entry::build()
            .title("foo")
            .description("bar")
            .lat(5.0)
            .lng(5.0)
            .finish();
        assert_eq!(e.in_bbox(&bb), true);
        let e = Entry::build()
            .title("foo")
            .description("bar")
            .lat(10.1)
            .lng(10.0)
            .finish();
        assert_eq!(e.in_bbox(&bb), false);
    }

    #[test]
    fn is_in_invalid_bounding_box() {
        let bb = vec![Coordinate {
                          lat: 10.0,
                          lng: 10.0,
                      }];
        let e = Entry::build()
            .lat(5.0)
            .lng(5.0)
            .finish();
        assert_eq!(e.in_bbox(&bb), false);
    }

    #[test]
    fn filter_by_bounding_box() {
        let bb = vec![Coordinate {
                          lat: -10.0,
                          lng: -10.0,
                      },
                      Coordinate {
                          lat: 10.0,
                          lng: 10.0,
                      }];
        let entries = vec![
            Entry::build().lat(5.0).lng(5.0).finish(),
            Entry::build().lat(-5.0).lng(5.0).finish(),
            Entry::build().lat(10.0).lng(10.1).finish(),
        ];
        assert_eq!(entries.iter().filter(|&x| x.in_bbox(&bb)).collect::<Vec<&Entry>>().len(),
                   2);
    }

    #[test]
    fn filter_by_category() {
        let entries = vec![
            Entry::build().categories(vec!["a"]).finish(),
            Entry::build().categories(vec!["c"]).finish(),
            Entry::build().categories(vec!["b","a"]).finish(),
        ];
        let ab = vec!["a".into(), "b".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_category_ids(&ab)).collect();
        assert_eq!(x.len(), 2);
        let b = vec!["b".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_category_ids(&b)).collect();
        assert_eq!(x.len(), 1);
        let c = vec!["c".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_category_ids(&c)).collect();
        assert_eq!(x.len(), 1);
    }

    #[test]
    fn search_entries_by_title() {
        let entries = vec![
            Entry::build().title("a title").description("x").finish(),
            Entry::build().title("not so interesting").description("y").finish(),
        ];
        let filter = entries_by_search_text("a");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].title, "a title");
        let filter = entries_by_search_text("ti");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 2);
    }

    #[test]
    fn search_entries_by_description() {
        let entries = vec![
            Entry::build().title("a").description("x").finish(),
            Entry::build().title("b").description("y").finish(),
            Entry::build().title("c").description("x").finish(),
        ];
        let filter = entries_by_search_text("x");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 2);
    }

    #[test]
    fn search_with_multiple_words() {
        let entries = vec![
            Entry::build().title("SoLaWi").description("mit gemüse").finish(),
            Entry::build().title("csa").description("This is a great csa").finish(),
            Entry::build().title("solawi").description("Das ist eine tolle solawi").finish(),
        ];
        let filter = entries_by_search_text("csa,toll");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 2);
        let filter = entries_by_search_text("great,to,gemü");
        let x: Vec<&Entry> = entries.iter().filter(&*filter).collect();
        assert_eq!(x.len(), 3);
    }

    #[test]
    fn search_and_ignore_capitalisation() {
        let e0 = Entry::build().title("Eintrag").description("Hallo! Ein EinTrag").finish();
        let e1 = Entry::build().title("Ein trag").description("foo").finish();
        let e2 = Entry::build().title("CSA").description("cool vegetables").finish();
        let entries = vec![&e0, &e1, &e2];

        let filter = entries_by_search_text("Foo");
        let x: Vec<&Entry> = entries.iter().cloned().filter(&*filter).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].id, e1.id);

        let filter = entries_by_search_text("trag");
        let x: Vec<&Entry> = entries.iter().cloned().filter(&*filter).collect();
        assert_eq!(x.len(), 2);
        assert_eq!(x[0].id, e0.id);
        assert_eq!(x[1].id, e1.id);

        let filter = entries_by_search_text("csa");
        let x: Vec<&Entry> = entries.iter().cloned().filter(&*filter).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].id, e2.id);
    }

    #[test]
    fn filter_by_tags() {
        let entries = vec![
            Entry::build().id("a").finish(),
            Entry::build().id("b").finish(),
            Entry::build().id("c").finish(),
        ];
        let tags = vec!["csa".into()];
        let no_triples = vec![];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags(&tags,&no_triples, Combination::Or)).collect();
        assert_eq!(x.len(), 0);
        let triples = vec![
            Triple{ subject: ObjectId::Entry("b".into()), predicate: Relation::IsTaggedWith, object: ObjectId::Tag("csa".into())},
            Triple{ subject: ObjectId::Entry("c".into()), predicate: Relation::IsTaggedWith, object: ObjectId::Tag("foo".into())}
        ];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags(&tags,&triples, Combination::Or)).collect();
        assert_eq!(x.len(), 1);
        assert_eq!(x[0].id,"b");
        let tags = vec!["csa".into(),"foo".into()];
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags(&tags,&triples, Combination::Or)).collect();
        assert_eq!(x.len(), 2);
        let x: Vec<&Entry> = entries.iter().filter(&*entries_by_tags(&tags,&triples, Combination::And)).collect();
        assert_eq!(x.len(), 0);
    }
}
<|endoftext|>


<commit_before>// Sync available crate categories from `src/categories.toml`.
// Runs when the server is started.

use diesel;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use toml;

use db;
use schema::categories;
use util::errors::{CargoResult, ChainError, internal};

#[derive(Debug)]
struct Category {
    slug: String,
    name: String,
    description: String,
}

impl Category {
    fn from_parent(
        slug: &str,
        name: &str,
        description: &str,
        parent: Option<&Category>,
    ) -> Category {
        match parent {
            Some(parent) => {
                Category {
                    slug: format!("{}::{}", parent.slug, slug),
                    name: format!("{}::{}", parent.name, name),
                    description: description.into(),
                }
            }
            None => {
                Category {
                    slug: slug.into(),
                    name: name.into(),
                    description: description.into(),
                }
            }
        }
    }
}

fn required_string_from_toml<'a>(toml: &'a toml::value::Table, key: &str) -> CargoResult<&'a str> {
    toml.get(key).and_then(toml::Value::as_str).chain_error(|| {
        internal(&format_args!(
            "Expected category TOML attribute '{}' to be a String",
            key
        ))
    })
}

fn optional_string_from_toml<'a>(toml: &'a toml::value::Table, key: &str) -> &'a str {
    toml.get(key).and_then(toml::Value::as_str).unwrap_or("")
}

fn categories_from_toml(
    categories: &toml::value::Table,
    parent: Option<&Category>,
) -> CargoResult<Vec<Category>> {
    let mut result = vec![];

    for (slug, details) in categories {
        let details = details.as_table().chain_error(|| {
            internal(&format_args!("category {} was not a TOML table", slug))
        })?;

        let category = Category::from_parent(
            slug,
            required_string_from_toml(details, "name")?,
            optional_string_from_toml(details, "description"),
            parent,
        );

        if let Some(categories) = details.get("categories") {
            let categories = categories.as_table().chain_error(|| {
                internal(&format_args!(
                    "child categories of {} were not a table",
                    slug
                ))
            })?;

            result.extend(categories_from_toml(categories, Some(&category))?);
        }

        result.push(category)
    }

    Ok(result)
}

#[derive(Insertable)]
#[table_name = "categories"]
struct NewCategory {
    slug: String,
    category: String,
    description: String,
}

pub fn sync(toml_str: &str) -> CargoResult<()> {
    let conn = db::connect_now().unwrap();
    sync_with_connection(toml_str, &conn)
}

pub fn sync_with_connection(toml_str: &str, conn: &PgConnection)  -> CargoResult<()> {
    use diesel::pg::upsert::*;
    use diesel::expression::dsl::all;

    let toml: toml::value::Table =
        toml::from_str(toml_str).expect("Could not parse categories toml");

    let categories = categories_from_toml(&toml, None)
        .expect("Could not convert categories from TOML")
        .into_iter()
        .map(|c| {
            NewCategory {
                slug: c.slug.to_lowercase(),
                category: c.name,
                description: c.description,
            }
        })
        .collect::<Vec<_>>();

    let to_insert = categories.on_conflict(
        categories::slug,
        do_update().set((
            categories::category.eq(excluded(categories::category)),
            categories::description.eq(
                excluded(categories::description),
            ),
        )),
    );

    conn.transaction(|| {
        let slugs = diesel::insert(&to_insert)
            .into(categories::table)
            .returning(categories::slug)
            .get_results::<String>(&*conn)?;

        let to_delete = categories::table.filter(categories::slug.ne(all(slugs)));
        diesel::delete(to_delete).execute(&*conn)?;
        Ok(())
    })
}
<commit_msg>Add missing debug<commit_after>// Sync available crate categories from `src/categories.toml`.
// Runs when the server is started.

use diesel;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use toml;

use db;
use schema::categories;
use util::errors::{CargoResult, ChainError, internal};

#[derive(Debug)]
struct Category {
    slug: String,
    name: String,
    description: String,
}

impl Category {
    fn from_parent(
        slug: &str,
        name: &str,
        description: &str,
        parent: Option<&Category>,
    ) -> Category {
        match parent {
            Some(parent) => {
                Category {
                    slug: format!("{}::{}", parent.slug, slug),
                    name: format!("{}::{}", parent.name, name),
                    description: description.into(),
                }
            }
            None => {
                Category {
                    slug: slug.into(),
                    name: name.into(),
                    description: description.into(),
                }
            }
        }
    }
}

fn required_string_from_toml<'a>(toml: &'a toml::value::Table, key: &str) -> CargoResult<&'a str> {
    toml.get(key).and_then(toml::Value::as_str).chain_error(|| {
        internal(&format_args!(
            "Expected category TOML attribute '{}' to be a String",
            key
        ))
    })
}

fn optional_string_from_toml<'a>(toml: &'a toml::value::Table, key: &str) -> &'a str {
    toml.get(key).and_then(toml::Value::as_str).unwrap_or("")
}

fn categories_from_toml(
    categories: &toml::value::Table,
    parent: Option<&Category>,
) -> CargoResult<Vec<Category>> {
    let mut result = vec![];

    for (slug, details) in categories {
        let details = details.as_table().chain_error(|| {
            internal(&format_args!("category {} was not a TOML table", slug))
        })?;

        let category = Category::from_parent(
            slug,
            required_string_from_toml(details, "name")?,
            optional_string_from_toml(details, "description"),
            parent,
        );

        if let Some(categories) = details.get("categories") {
            let categories = categories.as_table().chain_error(|| {
                internal(&format_args!(
                    "child categories of {} were not a table",
                    slug
                ))
            })?;

            result.extend(categories_from_toml(categories, Some(&category))?);
        }

        result.push(category)
    }

    Ok(result)
}

#[derive(Insertable, Debug)]
#[table_name = "categories"]
struct NewCategory {
    slug: String,
    category: String,
    description: String,
}

pub fn sync(toml_str: &str) -> CargoResult<()> {
    let conn = db::connect_now().unwrap();
    sync_with_connection(toml_str, &conn)
}

pub fn sync_with_connection(toml_str: &str, conn: &PgConnection)  -> CargoResult<()> {
    use diesel::pg::upsert::*;
    use diesel::expression::dsl::all;

    let toml: toml::value::Table =
        toml::from_str(toml_str).expect("Could not parse categories toml");

    let categories = categories_from_toml(&toml, None)
        .expect("Could not convert categories from TOML")
        .into_iter()
        .map(|c| {
            NewCategory {
                slug: c.slug.to_lowercase(),
                category: c.name,
                description: c.description,
            }
        })
        .collect::<Vec<_>>();

    let to_insert = categories.on_conflict(
        categories::slug,
        do_update().set((
            categories::category.eq(excluded(categories::category)),
            categories::description.eq(
                excluded(categories::description),
            ),
        )),
    );

    conn.transaction(|| {
        let slugs = diesel::insert(&to_insert)
            .into(categories::table)
            .returning(categories::slug)
            .get_results::<String>(&*conn)?;

        let to_delete = categories::table.filter(categories::slug.ne(all(slugs)));
        diesel::delete(to_delete).execute(&*conn)?;
        Ok(())
    })
}
<|endoftext|>


<commit_before>use crypt32;
use std::io;
use std::ptr;
use winapi;

use cert_context::CertContext;
use ctl_context::CtlContext;

use Inner;

pub struct CertStore(winapi::HCERTSTORE);

unsafe impl Send for CertStore { }

impl Drop for CertStore {
	fn drop(&mut self) {
		unsafe {
			crypt32::CertCloseStore(self.0, 0);
		}
	}
}

impl Clone for CertStore {
	fn clone(&self) -> CertStore {
		unsafe {
			CertStore(crypt32::CertDuplicateStore(self.0))
		}
	}
}

impl Inner<winapi::HCERTSTORE> for CertStore {
	unsafe fn from_inner(t: winapi::HCERTSTORE) -> CertStore {
		CertStore(t)
	}

	fn as_inner(&self) -> winapi::HCERTSTORE {
		self.0
	}
}

impl CertStore {
	pub fn memory() -> io::Result<Memory> {
		unsafe {
			let store = crypt32::CertOpenStore(winapi::CERT_STORE_PROV_MEMORY as winapi::LPCSTR,
											   0,
											   0,
											   0,
											   ptr::null_mut());
			if store.is_null() {
				Err(io::Error::last_os_error())
			} else {
				Ok(Memory(CertStore(store)))
			}
		}
	}
}

pub struct Memory(CertStore);

impl Memory {
	pub fn add_der_certificate(&mut self, cert: &[u8]) -> io::Result<CertContext> {
		unsafe {
			let mut cert_context = ptr::null();

			let res = crypt32::CertAddEncodedCertificateToStore(
				(self.0).0,
				winapi::X509_ASN_ENCODING | winapi::PKCS_7_ASN_ENCODING,
				cert.as_ptr() as *const _,
				cert.len() as winapi::DWORD,
				winapi::CERT_STORE_ADD_ALWAYS,
				&mut cert_context);
			if res == winapi::TRUE {
				Ok(CertContext::from_inner(cert_context))
			} else {
				Err(io::Error::last_os_error())
			}
		}
	}

	pub fn add_der_ctl(&mut self, ctl: &[u8]) -> io::Result<CtlContext> {
		unsafe {
			let mut ctl_context = ptr::null();

			let res = crypt32::CertAddEncodedCTLToStore(
				(self.0).0,
				winapi::X509_ASN_ENCODING | winapi::PKCS_7_ASN_ENCODING,
				ctl.as_ptr() as *const _,
				ctl.len() as winapi::DWORD,
				winapi::CERT_STORE_ADD_ALWAYS,
				&mut ctl_context);
			if res == winapi::TRUE {
				Ok(CtlContext::from_inner(ctl_context))
			} else {
				Err(io::Error::last_os_error())
			}
		}
	}

	pub fn into_store(self) -> CertStore {
		self.0
	}
}

#[cfg(test)]
mod test {
	use std::fs::File;
	use std::io::Read;

	use super::*;

	#[test]
	fn load() {
		let mut file = File::open(concat!(env!("CARGO_MANIFEST_DIR"), "/test/cert.der"))
				           .unwrap();
		let mut cert = vec![];
		file.read_to_end(&mut cert).unwrap();

		let mut store = CertStore::memory().unwrap();
		store.add_der_certificate(&cert).unwrap();
	}
}
<commit_msg>Implement Sync for CertStore<commit_after>use crypt32;
use std::io;
use std::ptr;
use winapi;

use cert_context::CertContext;
use ctl_context::CtlContext;

use Inner;

pub struct CertStore(winapi::HCERTSTORE);

unsafe impl Sync for CertStore {}
unsafe impl Send for CertStore {}

impl Drop for CertStore {
	fn drop(&mut self) {
		unsafe {
			crypt32::CertCloseStore(self.0, 0);
		}
	}
}

impl Clone for CertStore {
	fn clone(&self) -> CertStore {
		unsafe {
			CertStore(crypt32::CertDuplicateStore(self.0))
		}
	}
}

impl Inner<winapi::HCERTSTORE> for CertStore {
	unsafe fn from_inner(t: winapi::HCERTSTORE) -> CertStore {
		CertStore(t)
	}

	fn as_inner(&self) -> winapi::HCERTSTORE {
		self.0
	}
}

impl CertStore {
	pub fn memory() -> io::Result<Memory> {
		unsafe {
			let store = crypt32::CertOpenStore(winapi::CERT_STORE_PROV_MEMORY as winapi::LPCSTR,
											   0,
											   0,
											   0,
											   ptr::null_mut());
			if store.is_null() {
				Err(io::Error::last_os_error())
			} else {
				Ok(Memory(CertStore(store)))
			}
		}
	}
}

pub struct Memory(CertStore);

impl Memory {
	pub fn add_der_certificate(&mut self, cert: &[u8]) -> io::Result<CertContext> {
		unsafe {
			let mut cert_context = ptr::null();

			let res = crypt32::CertAddEncodedCertificateToStore(
				(self.0).0,
				winapi::X509_ASN_ENCODING | winapi::PKCS_7_ASN_ENCODING,
				cert.as_ptr() as *const _,
				cert.len() as winapi::DWORD,
				winapi::CERT_STORE_ADD_ALWAYS,
				&mut cert_context);
			if res == winapi::TRUE {
				Ok(CertContext::from_inner(cert_context))
			} else {
				Err(io::Error::last_os_error())
			}
		}
	}

	pub fn add_der_ctl(&mut self, ctl: &[u8]) -> io::Result<CtlContext> {
		unsafe {
			let mut ctl_context = ptr::null();

			let res = crypt32::CertAddEncodedCTLToStore(
				(self.0).0,
				winapi::X509_ASN_ENCODING | winapi::PKCS_7_ASN_ENCODING,
				ctl.as_ptr() as *const _,
				ctl.len() as winapi::DWORD,
				winapi::CERT_STORE_ADD_ALWAYS,
				&mut ctl_context);
			if res == winapi::TRUE {
				Ok(CtlContext::from_inner(ctl_context))
			} else {
				Err(io::Error::last_os_error())
			}
		}
	}

	pub fn into_store(self) -> CertStore {
		self.0
	}
}

#[cfg(test)]
mod test {
	use std::fs::File;
	use std::io::Read;

	use super::*;

	#[test]
	fn load() {
		let mut file = File::open(concat!(env!("CARGO_MANIFEST_DIR"), "/test/cert.der"))
				           .unwrap();
		let mut cert = vec![];
		file.read_to_end(&mut cert).unwrap();

		let mut store = CertStore::memory().unwrap();
		store.add_der_certificate(&cert).unwrap();
	}
}
<|endoftext|>


<commit_before>//! Library used by tidy and other tools.
//!
//! This library contains the tidy lints and exposes it
//! to be used by tools.

use walkdir::{DirEntry, WalkDir};
use std::fs::File;
use std::io::Read;

use std::path::Path;

macro_rules! t {
    ($e:expr, $p:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e),
    });

    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

macro_rules! tidy_error {
    ($bad:expr, $fmt:expr, $($arg:tt)*) => ({
        *$bad = true;
        eprint!("tidy error: ");
        eprintln!($fmt, $($arg)*);
    });
}

pub mod bins;
pub mod style;
pub mod errors;
pub mod features;
pub mod cargo;
pub mod edition;
pub mod pal;
pub mod deps;
pub mod extdeps;
pub mod ui_tests;
pub mod unit_tests;
pub mod unstable_book;

fn filter_dirs(path: &Path) -> bool {
    let skip = [
        "src/llvm-emscripten",
        "src/llvm-project",
        "src/stdarch",
        "src/tools/cargo",
        "src/tools/clippy",
        "src/tools/miri",
        "src/tools/rls",
        "src/tools/rust-installer",
        "src/tools/rustfmt",
    ];
    skip.iter().any(|p| path.ends_with(p))
}

fn walk_many(
    paths: &[&Path], skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry, &str)
) {
    for path in paths {
        walk(path, skip, f);
    }
}

fn walk(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry, &str)) {
    let mut contents = String::new();
    walk_no_read(path, skip, &mut |entry| {
        contents.clear();
        if t!(File::open(entry.path()), entry.path()).read_to_string(&mut contents).is_err() {
            contents.clear();
        }
        f(&entry, &contents);
    });
}

fn walk_no_read(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry)) {
    let walker = WalkDir::new(path).into_iter()
        .filter_entry(|e| !skip(e.path()));
    for entry in walker {
        if let Ok(entry) = entry {
            if entry.file_type().is_dir() {
                continue;
            }
            f(&entry);
        }
    }
}
<commit_msg>Filter out RLS output directories on tidy runs<commit_after>//! Library used by tidy and other tools.
//!
//! This library contains the tidy lints and exposes it
//! to be used by tools.

use walkdir::{DirEntry, WalkDir};
use std::fs::File;
use std::io::Read;

use std::path::Path;

macro_rules! t {
    ($e:expr, $p:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed on {} with {}", stringify!($e), ($p).display(), e),
    });

    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

macro_rules! tidy_error {
    ($bad:expr, $fmt:expr, $($arg:tt)*) => ({
        *$bad = true;
        eprint!("tidy error: ");
        eprintln!($fmt, $($arg)*);
    });
}

pub mod bins;
pub mod style;
pub mod errors;
pub mod features;
pub mod cargo;
pub mod edition;
pub mod pal;
pub mod deps;
pub mod extdeps;
pub mod ui_tests;
pub mod unit_tests;
pub mod unstable_book;

fn filter_dirs(path: &Path) -> bool {
    let skip = [
        "src/llvm-emscripten",
        "src/llvm-project",
        "src/stdarch",
        "src/tools/cargo",
        "src/tools/clippy",
        "src/tools/miri",
        "src/tools/rls",
        "src/tools/rust-installer",
        "src/tools/rustfmt",

        // Filter RLS output directories
        "target/rls",
    ];
    skip.iter().any(|p| path.ends_with(p))
}

fn walk_many(
    paths: &[&Path], skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry, &str)
) {
    for path in paths {
        walk(path, skip, f);
    }
}

fn walk(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry, &str)) {
    let mut contents = String::new();
    walk_no_read(path, skip, &mut |entry| {
        contents.clear();
        if t!(File::open(entry.path()), entry.path()).read_to_string(&mut contents).is_err() {
            contents.clear();
        }
        f(&entry, &contents);
    });
}

fn walk_no_read(path: &Path, skip: &mut dyn FnMut(&Path) -> bool, f: &mut dyn FnMut(&DirEntry)) {
    let walker = WalkDir::new(path).into_iter()
        .filter_entry(|e| !skip(e.path()));
    for entry in walker {
        if let Ok(entry) = entry {
            if entry.file_type().is_dir() {
                continue;
            }
            f(&entry);
        }
    }
}
<|endoftext|>


<commit_before>use pact_matching::models::{Pact, Interaction, Request, OptionalBody, PactSpecification};
use pact_matching::models::matchingrules::*;
use pact_matching::models::generators::*;
use pact_matching::models::parse_query_string;

use std::collections::{BTreeMap, HashMap};
use log::{log, debug, warn};
use hyper::{Body, Response, Server, Error};
use hyper::service::service_fn_ok;
use futures::future::Future;
use futures::stream::Stream;

fn extract_path(uri: &hyper::Uri) -> String {
    uri.path_and_query()
        .map(|path_and_query| path_and_query.path())
        .unwrap_or("/")
        .into()
}

fn extract_query_string(uri: &hyper::Uri) -> Option<HashMap<String, Vec<String>>> {
    uri.path_and_query()
        .and_then(|path_and_query| path_and_query.query())
        .and_then(|query| parse_query_string(&query.into()))
}

fn extract_headers(headers: &hyper::HeaderMap) -> Option<HashMap<String, String>> {
    if headers.len() > 0 {
        let v = headers.iter().map(|(name, value)| -> Result<(String, String), String> {
            Ok((name.as_str().into(), value.to_string()?))
        });
        None
    } else {
        None
    }
}

pub fn extract_body(chunk: hyper::Chunk) -> OptionalBody {
    let bytes = chunk.into_bytes();
    if bytes.len() > 0 {
        OptionalBody::Present(bytes.to_vec())
    } else {
        OptionalBody::Empty
    }
}

fn hyper_request_to_pact_request(req: hyper::Request<Body>) -> impl Future<Item = Request, Error = hyper::Error> {
    let method = req.method().to_string();
    let path = extract_path(req.uri());
    let query = extract_query_string(req.uri());
    let headers = extract_headers(req.headers());

    req.into_body()
        .concat2()
        .and_then(|body_chunk| Ok(Request {
            method: req.method().to_string(),
            path: path,
            query: query,
            headers: headers,
            body: extract_body(body_chunk),
            matching_rules: MatchingRules::default(),
            generators: Generators::default()
        }))
}

pub fn start(
    id: String,
    pact: Pact,
    port: u16,
    shutdown: impl Future<Item = (), Error = ()>,
) -> (impl Future<Item = (), Error = Error>, u16) {
    let addr = ([0, 0, 0, 0], port).into();
    let server = Server::bind(&addr)
        .serve(|| {
            service_fn_ok(|req| {
                debug!("Creating pact request from hyper request");
                let req = hyper_request_to_pact_request(req);
                Response::new(Body::from("Hello World"))
            })
        });

    let port = server.local_addr().port();

    (server.with_graceful_shutdown(shutdown), port)
}
<commit_msg>Fix extract_headers (can error)<commit_after>use pact_matching::models::{Pact, Interaction, Request, OptionalBody, PactSpecification};
use pact_matching::models::matchingrules::*;
use pact_matching::models::generators::*;
use pact_matching::models::parse_query_string;

use std::collections::{BTreeMap, HashMap};
use log::{log, debug, warn};
use hyper::{Body, Response, Server, Error};
use hyper::header::ToStrError;
use hyper::service::service_fn_ok;
use futures::future::Future;
use futures::stream::Stream;

fn extract_path(uri: &hyper::Uri) -> String {
    uri.path_and_query()
        .map(|path_and_query| path_and_query.path())
        .unwrap_or("/")
        .into()
}

fn extract_query_string(uri: &hyper::Uri) -> Option<HashMap<String, Vec<String>>> {
    uri.path_and_query()
        .and_then(|path_and_query| path_and_query.query())
        .and_then(|query| parse_query_string(&query.into()))
}

fn extract_headers(headers: &hyper::HeaderMap) -> Result<Option<HashMap<String, String>>, ToStrError> {
    if headers.len() > 0 {
        let result: Result<HashMap<String, String>, ToStrError> = headers.keys()
            .map(|name| -> Result<(String, String), ToStrError> {
                let values = headers.get_all(name);
                let mut iter = values.iter();

                let first_value = iter.next().unwrap();

                if iter.next().is_some() {
                    warn!("Multiple headers associated with '{}', but only the first is matched", name);
                }

                Ok((name.as_str().into(), first_value.to_str()?.into()))
            })
            .collect();

        result.map(|map| Some(map))
    } else {
        Ok(None)
    }
}

pub fn extract_body(chunk: hyper::Chunk) -> OptionalBody {
    let bytes = chunk.into_bytes();
    if bytes.len() > 0 {
        OptionalBody::Present(bytes.to_vec())
    } else {
        OptionalBody::Empty
    }
}

fn hyper_request_to_pact_request(req: hyper::Request<Body>) -> impl Future<Item = Request, Error = hyper::Error> {
    let method = req.method().to_string();
    let path = extract_path(req.uri());
    let query = extract_query_string(req.uri());
    let headers = extract_headers(req.headers());

    req.into_body()
        .concat2()
        .and_then(|body_chunk| Ok(Request {
            method: req.method().to_string(),
            path: path,
            query: query,
            headers: headers,
            body: extract_body(body_chunk),
            matching_rules: MatchingRules::default(),
            generators: Generators::default()
        }))
}

pub fn start(
    id: String,
    pact: Pact,
    port: u16,
    shutdown: impl Future<Item = (), Error = ()>,
) -> (impl Future<Item = (), Error = Error>, u16) {
    let addr = ([0, 0, 0, 0], port).into();
    let server = Server::bind(&addr)
        .serve(|| {
            service_fn_ok(|req| {
                debug!("Creating pact request from hyper request");
                let req = hyper_request_to_pact_request(req);
                Response::new(Body::from("Hello World"))
            })
        });

    let port = server.local_addr().port();

    (server.with_graceful_shutdown(shutdown), port)
}
<|endoftext|>


<commit_before>extern crate gtk;
use gtk::prelude::*;

use std::rc::Rc;
use std::cell::RefCell;

use ::media::{Context, Timestamp};

use super::{ImageSurface};

pub struct InfoController {
    drawingarea: gtk::DrawingArea,

    title_lbl: gtk::Label,
    artist_lbl: gtk::Label,
    container_lbl: gtk::Label,
    audio_codec_lbl: gtk::Label,
    video_codec_lbl: gtk::Label,
    duration_lbl: gtk::Label,
    position_lbl: gtk::Label,
    timeline_scale: gtk::Scale,

    chapter_treeview: gtk::TreeView,
    chapter_store: gtk::ListStore,
    chapter_starts: Vec<(u64, usize)>,
    current_chapter_id: usize,

    thumbnail: Rc<RefCell<Option<ImageSurface>>>,
}

impl InfoController {
    pub fn new(builder: &gtk::Builder) -> Self {
        // need a RefCell because the callbacks will use immutable versions of ac
        // when the UI controllers will get a mutable version from time to time
        let this = InfoController {
            drawingarea: builder.get_object("thumbnail-drawingarea").unwrap(),

            title_lbl: builder.get_object("title-lbl").unwrap(),
            artist_lbl: builder.get_object("artist-lbl").unwrap(),
            container_lbl: builder.get_object("container-lbl").unwrap(),
            audio_codec_lbl: builder.get_object("audio_codec-lbl").unwrap(),
            video_codec_lbl: builder.get_object("video_codec-lbl").unwrap(),
            duration_lbl: builder.get_object("duration-lbl").unwrap(),
            position_lbl: builder.get_object("position-lbl").unwrap(),
            timeline_scale: builder.get_object("timeline-scale").unwrap(),

            chapter_treeview: builder.get_object("chapter-treeview").unwrap(),
            // columns: Id, Title, Start, End
            chapter_store: gtk::ListStore::new(&[gtk::Type::I32, gtk::Type::String, gtk::Type::String, gtk::Type::String]),
            chapter_starts: Vec::<(u64, usize)>::new(),
            current_chapter_id: ::std::usize::MAX,

            thumbnail: Rc::new(RefCell::new(None)),
        };

        this.chapter_treeview.set_model(Some(&this.chapter_store));
        this.add_chapter_column("Id", 0, false);
        this.add_chapter_column("Title", 1, true);
        this.add_chapter_column("Start", 2, false);
        this.add_chapter_column("End", 3, false);

        let thumbnail_weak = Rc::downgrade(&this.thumbnail);
        this.drawingarea.connect_draw(move |drawing_area, cairo_ctx| {
            if let Some(thumbnail_rc) = thumbnail_weak.upgrade() {
                let thumbnail_opt = thumbnail_rc.borrow();
                if let Some(ref thumbnail) = *thumbnail_opt {
                    let surface = &thumbnail.surface;

                    let allocation = drawing_area.get_allocation();
                    let alloc_ratio = allocation.width as f64 / allocation.height as f64;
                    let surface_ratio = surface.get_width() as f64 / surface.get_height() as f64;
                    let scale = if surface_ratio < alloc_ratio {
                        allocation.height as f64 / surface.get_height() as f64
                    }
                    else {
                        allocation.width as f64 / surface.get_width() as f64
                    };
                    let x = (allocation.width as f64 / scale - surface.get_width() as f64).abs() / 2f64;
                    let y = (allocation.height as f64 / scale - surface.get_height() as f64).abs() / 2f64;

                    cairo_ctx.scale(scale, scale);
                    cairo_ctx.set_source_surface(surface, x, y);
                    cairo_ctx.paint();
                }
            }

            Inhibit(true)
        });

        this
    }

    fn add_chapter_column(&self, title: &str, col_id: i32, can_expand: bool) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);
        let renderer = gtk::CellRendererText::new();
        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id);
        if can_expand {
            col.set_min_width(70);
            col.set_expand(can_expand);
        }
        self.chapter_treeview.append_column(&col);
    }

    pub fn new_media(&mut self, context: &Context) {
        let duration = context.get_duration();
        self.timeline_scale.set_range(0f64, duration as f64);
        self.duration_lbl.set_label(
            &format!("{}", Timestamp::format(duration))
        );

        self.chapter_store.clear();

        let mut has_image = false;
        {
            let mut info = context.info.lock()
                .expect("Failed to lock media info in InfoController");

            if let Some(thumbnail) = info.thumbnail.take() {
                if let Ok(image) = ImageSurface::from_aligned_image(thumbnail) {
                    let mut thumbnail_ref = self.thumbnail.borrow_mut();
                    *thumbnail_ref = Some(image);
                    has_image = true;
                }
            };

            self.title_lbl.set_label(&info.title);
            self.artist_lbl.set_label(&info.artist);
            // Fix container for mp3 audio files TODO: move this to MediaInfo
            let container = if info.video_codec.is_empty()
                && info.audio_codec.to_lowercase().find("mp3").is_some()
            {
                "MP3"
            }
            else {
                &info.container
            };
            self.container_lbl.set_label(container);
            self.audio_codec_lbl.set_label(
                if !info.audio_codec.is_empty() { &info.audio_codec } else { "-" }
            );
            self.video_codec_lbl.set_label(
                if !info.video_codec.is_empty() { &info.video_codec } else { "-" }
            );

            self.timeline_scale.clear_marks();
            self.chapter_starts.clear();

            // FIX for sample.mkv video: generate ids (TODO: remove)
            for (id, chapter) in info.chapters.iter().enumerate() {
                self.timeline_scale.add_mark(
                    chapter.start.nano_total as f64,
                    gtk::PositionType::Top,
                    None
                );
                self.chapter_store.insert_with_values(
                    None, &[0, 1, 2, 3],
                    &[&((id+1) as u32), &chapter.title(), &format!("{}", &chapter.start), &format!("{}", chapter.end)],
                );
                self.chapter_starts.push((chapter.start.nano_total, id));
            }

            // reverse chapters in order to ease seaching
            self.chapter_starts.reverse();
            self.current_chapter_id = ::std::usize::MAX;
        }

        if has_image {
            self.drawingarea.show();
            self.drawingarea.queue_draw();
        }
        else {
            self.drawingarea.hide();
        }
    }

    pub fn cleanup(&mut self) {
        self.title_lbl.set_text("");
        self.artist_lbl.set_text("");
        self.container_lbl.set_text("");
        self.audio_codec_lbl.set_text("");
        self.video_codec_lbl.set_text("");
        self.duration_lbl.set_text("");
        self.chapter_store.clear();
        self.timeline_scale.clear_marks();
        self.timeline_scale.set_value(0f64);
        self.position_lbl.set_text("");
    }

    pub fn tic(&mut self, position: u64) {
        self.timeline_scale.set_value(position as f64);
        self.position_lbl.set_text(&Timestamp::format(position));

        // reminder: chapters order is reversed
        for &(start, id) in self.chapter_starts.iter() {
            if start <= position {
                // found current chapter
                if self.current_chapter_id != id {
                    if let Some(store_iter) =
                        self.chapter_store.get_iter(
                            &gtk::TreePath::new_from_indicesv(&[id as i32])
                        )
                    {
                        self.chapter_treeview.get_selection()
                            .select_iter(&store_iter);
                    };

                    self.current_chapter_id = id;
                }

                break;
            }
        }
    }
}
<commit_msg>Fix init values for time position and duration (#14)<commit_after>extern crate gtk;
use gtk::prelude::*;

use std::rc::Rc;
use std::cell::RefCell;

use ::media::{Context, Timestamp};

use super::{ImageSurface};

pub struct InfoController {
    drawingarea: gtk::DrawingArea,

    title_lbl: gtk::Label,
    artist_lbl: gtk::Label,
    container_lbl: gtk::Label,
    audio_codec_lbl: gtk::Label,
    video_codec_lbl: gtk::Label,
    duration_lbl: gtk::Label,
    position_lbl: gtk::Label,
    timeline_scale: gtk::Scale,

    chapter_treeview: gtk::TreeView,
    chapter_store: gtk::ListStore,
    chapter_starts: Vec<(u64, usize)>,
    current_chapter_id: usize,

    thumbnail: Rc<RefCell<Option<ImageSurface>>>,
}

impl InfoController {
    pub fn new(builder: &gtk::Builder) -> Self {
        // need a RefCell because the callbacks will use immutable versions of ac
        // when the UI controllers will get a mutable version from time to time
        let mut this = InfoController {
            drawingarea: builder.get_object("thumbnail-drawingarea").unwrap(),

            title_lbl: builder.get_object("title-lbl").unwrap(),
            artist_lbl: builder.get_object("artist-lbl").unwrap(),
            container_lbl: builder.get_object("container-lbl").unwrap(),
            audio_codec_lbl: builder.get_object("audio_codec-lbl").unwrap(),
            video_codec_lbl: builder.get_object("video_codec-lbl").unwrap(),
            duration_lbl: builder.get_object("duration-lbl").unwrap(),
            position_lbl: builder.get_object("position-lbl").unwrap(),
            timeline_scale: builder.get_object("timeline-scale").unwrap(),

            chapter_treeview: builder.get_object("chapter-treeview").unwrap(),
            // columns: Id, Title, Start, End
            chapter_store: gtk::ListStore::new(&[gtk::Type::I32, gtk::Type::String, gtk::Type::String, gtk::Type::String]),
            chapter_starts: Vec::<(u64, usize)>::new(),
            current_chapter_id: ::std::usize::MAX,

            thumbnail: Rc::new(RefCell::new(None)),
        };

        this.cleanup();

        this.chapter_treeview.set_model(Some(&this.chapter_store));
        this.add_chapter_column("Id", 0, false);
        this.add_chapter_column("Title", 1, true);
        this.add_chapter_column("Start", 2, false);
        this.add_chapter_column("End", 3, false);

        let thumbnail_weak = Rc::downgrade(&this.thumbnail);
        this.drawingarea.connect_draw(move |drawing_area, cairo_ctx| {
            if let Some(thumbnail_rc) = thumbnail_weak.upgrade() {
                let thumbnail_opt = thumbnail_rc.borrow();
                if let Some(ref thumbnail) = *thumbnail_opt {
                    let surface = &thumbnail.surface;

                    let allocation = drawing_area.get_allocation();
                    let alloc_ratio = allocation.width as f64 / allocation.height as f64;
                    let surface_ratio = surface.get_width() as f64 / surface.get_height() as f64;
                    let scale = if surface_ratio < alloc_ratio {
                        allocation.height as f64 / surface.get_height() as f64
                    }
                    else {
                        allocation.width as f64 / surface.get_width() as f64
                    };
                    let x = (allocation.width as f64 / scale - surface.get_width() as f64).abs() / 2f64;
                    let y = (allocation.height as f64 / scale - surface.get_height() as f64).abs() / 2f64;

                    cairo_ctx.scale(scale, scale);
                    cairo_ctx.set_source_surface(surface, x, y);
                    cairo_ctx.paint();
                }
            }

            Inhibit(true)
        });

        this
    }

    fn add_chapter_column(&self, title: &str, col_id: i32, can_expand: bool) {
        let col = gtk::TreeViewColumn::new();
        col.set_title(title);
        let renderer = gtk::CellRendererText::new();
        col.pack_start(&renderer, true);
        col.add_attribute(&renderer, "text", col_id);
        if can_expand {
            col.set_min_width(70);
            col.set_expand(can_expand);
        }
        self.chapter_treeview.append_column(&col);
    }

    pub fn new_media(&mut self, context: &Context) {
        let duration = context.get_duration();
        self.timeline_scale.set_range(0f64, duration as f64);
        self.duration_lbl.set_label(
            &format!("{}", Timestamp::format(duration))
        );

        self.chapter_store.clear();

        let mut has_image = false;
        {
            let mut info = context.info.lock()
                .expect("Failed to lock media info in InfoController");

            if let Some(thumbnail) = info.thumbnail.take() {
                if let Ok(image) = ImageSurface::from_aligned_image(thumbnail) {
                    let mut thumbnail_ref = self.thumbnail.borrow_mut();
                    *thumbnail_ref = Some(image);
                    has_image = true;
                }
            };

            self.title_lbl.set_label(&info.title);
            self.artist_lbl.set_label(&info.artist);
            // Fix container for mp3 audio files TODO: move this to MediaInfo
            let container = if info.video_codec.is_empty()
                && info.audio_codec.to_lowercase().find("mp3").is_some()
            {
                "MP3"
            }
            else {
                &info.container
            };
            self.container_lbl.set_label(container);
            self.audio_codec_lbl.set_label(
                if !info.audio_codec.is_empty() { &info.audio_codec } else { "-" }
            );
            self.video_codec_lbl.set_label(
                if !info.video_codec.is_empty() { &info.video_codec } else { "-" }
            );

            self.timeline_scale.clear_marks();
            self.chapter_starts.clear();

            // FIX for sample.mkv video: generate ids (TODO: remove)
            for (id, chapter) in info.chapters.iter().enumerate() {
                self.timeline_scale.add_mark(
                    chapter.start.nano_total as f64,
                    gtk::PositionType::Top,
                    None
                );
                self.chapter_store.insert_with_values(
                    None, &[0, 1, 2, 3],
                    &[&((id+1) as u32), &chapter.title(), &format!("{}", &chapter.start), &format!("{}", chapter.end)],
                );
                self.chapter_starts.push((chapter.start.nano_total, id));
            }

            // reverse chapters in order to ease seaching
            self.chapter_starts.reverse();
            self.current_chapter_id = ::std::usize::MAX;
        }

        if has_image {
            self.drawingarea.show();
            self.drawingarea.queue_draw();
        }
        else {
            self.drawingarea.hide();
        }
    }

    pub fn cleanup(&mut self) {
        self.title_lbl.set_text("");
        self.artist_lbl.set_text("");
        self.container_lbl.set_text("");
        self.audio_codec_lbl.set_text("");
        self.video_codec_lbl.set_text("");
        self.duration_lbl.set_text("00:00.000");
        self.chapter_store.clear();
        self.timeline_scale.clear_marks();
        self.timeline_scale.set_value(0f64);
        self.position_lbl.set_text("00:00.000");
    }

    pub fn tic(&mut self, position: u64) {
        self.timeline_scale.set_value(position as f64);
        self.position_lbl.set_text(&Timestamp::format(position));

        // reminder: chapters order is reversed
        for &(start, id) in self.chapter_starts.iter() {
            if start <= position {
                // found current chapter
                if self.current_chapter_id != id {
                    if let Some(store_iter) =
                        self.chapter_store.get_iter(
                            &gtk::TreePath::new_from_indicesv(&[id as i32])
                        )
                    {
                        self.chapter_treeview.get_selection()
                            .select_iter(&store_iter);
                    };

                    self.current_chapter_id = id;
                }

                break;
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate wayland_server;
extern crate smithay;

use smithay::shm::ShmGlobal;
use smithay::backend::glutin;
use wayland_server::protocol::wl_shm;

fn main() {
    let (_, mut event_loop) = wayland_server::create_display();

    // Insert the ShmGlobal as a handler to your event loop
    // Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported.
    let handler_id = event_loop.add_handler_with_init(ShmGlobal::new(
        vec![],
        None // we don't provide a logger here
    ));

    // Register this handler to advertise a wl_shm global of version 1
    let shm_global = event_loop.register_global::<wl_shm::WlShm,ShmGlobal>(handler_id, 1);

    // Retrieve the shm token for later use to access the buffers
    let shm_token = {
        let state = event_loop.state();
        state.get_handler::<ShmGlobal>(handler_id).get_token()
    };

    // Initialize a simple backend for testing
    let (mut renderer, mut input) = glutin::init_windowed().unwrap();

    //TODO render stuff

    //TODO put input handling on the event loop
    input.process_new_events();

    event_loop.run();
}
<commit_msg>Updated example<commit_after>extern crate wayland_server;
extern crate smithay;

use smithay::shm::ShmGlobal;
use smithay::backend::glutin;
use smithay::backend::input::InputBackend;
use wayland_server::protocol::wl_shm;

fn main() {
    let (_, mut event_loop) = wayland_server::create_display();

    // Insert the ShmGlobal as a handler to your event loop
    // Here, we specify tha the standard Argb8888 and Xrgb8888 is the only supported.
    let handler_id = event_loop.add_handler_with_init(ShmGlobal::new(
        vec![],
        None // we don't provide a logger here
    ));

    // Register this handler to advertise a wl_shm global of version 1
    let shm_global = event_loop.register_global::<wl_shm::WlShm,ShmGlobal>(handler_id, 1);

    // Retrieve the shm token for later use to access the buffers
    let shm_token = {
        let state = event_loop.state();
        state.get_handler::<ShmGlobal>(handler_id).get_token()
    };

    // Initialize a simple backend for testing
    let (mut renderer, mut input) = glutin::init_windowed().unwrap();

    //TODO render stuff

    //TODO put input handling on the event loop
    input.dispatch_new_events().unwrap();

    event_loop.run().unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2013 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern mod glfw;

use std::libc;

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
    std::rt::start_on_main_thread(argc, argv, crate_map, main)
}

fn main() {
    glfw::set_error_callback(error_callback);

    do glfw::start {
        let window = glfw::Window::create(300, 300, "Hello this is window", glfw::Windowed).unwrap();

        window.set_key_callback(key_callback);
        window.make_context_current();

        while !window.should_close() {
            glfw::poll_events();
        }
    }
}

fn key_callback(window: &glfw::Window, key: libc::c_int, _: libc::c_int, action: libc::c_int, _: libc::c_int) {
    if action == glfw::PRESS && key == glfw::KEY_ESCAPE {
        window.set_should_close(true);
    }
}

fn error_callback(_: libc::c_int, description: ~str) {
    println(fmt!("GLFW Error: %s", description));
}
<commit_msg>Add polling for window events (i.e., callbacks) to the window example<commit_after>// Copyright 2013 The GLFW-RS Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern mod glfw;

use std::libc;

#[start]
fn start(argc: int, argv: **u8, crate_map: *u8) -> int {
    std::rt::start_on_main_thread(argc, argv, crate_map, main)
}

fn main() {
    glfw::set_error_callback(error_callback);

    do glfw::start {
        let window = glfw::Window::create(300, 300, "Hello this is window", glfw::Windowed).unwrap();

        window.set_key_callback(key_callback);
        window.make_context_current();

        while !window.should_close() {
            window.poll_events();
            glfw::poll_events();
        }
    }
}

fn key_callback(window: &glfw::Window, key: libc::c_int, _: libc::c_int, action: libc::c_int, _: libc::c_int) {
    if action == glfw::PRESS && key == glfw::KEY_ESCAPE {
        window.set_should_close(true);
    }
}

fn error_callback(_: libc::c_int, description: ~str) {
    println(fmt!("GLFW Error: %s", description));
}
<|endoftext|>


<commit_before>//! Hyper client bindings for unix domain sockets

use std::io;

use futures::IntoFuture;
use futures::future::{self, FutureResult};
use hyper::Uri as HyperUri;
use tokio_core::reactor::Handle;
use tokio_service::Service;
use tokio_uds::UnixStream;

use super::Uri;

const UNIX_SCHEME: &str = "unix";

/// A type which implements hyper's client connector interface
/// for unix domain sockets
///
/// `UnixConnector` instances assume uri's
/// constructued with `hyperlocal::Uri::new()` which produce uris with a `unix://`
/// scheme
///
/// # examples
///
/// ```no_run
/// extern crate hyper;
/// extern crate hyperlocal;
/// extern crate tokio_core;
///
/// let core = tokio_core::reactor::Core::new().unwrap();
/// let client = hyper::Client::configure()
///    .connector(
///      hyperlocal::UnixConnector::new(core.handle())
///    )
///    .build(&core.handle());
/// ```
pub struct UnixConnector(Handle);

impl UnixConnector {
    pub fn new(handle: Handle) -> Self {
        UnixConnector(handle)
    }
}

impl Service for UnixConnector {
    type Request = HyperUri;
    type Response = UnixStream;
    type Error = io::Error;
    type Future = FutureResult<UnixStream, io::Error>;

    fn call(&self, uri: HyperUri) -> Self::Future {
        if uri.scheme() != Some(UNIX_SCHEME) {
            return future::err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("Invalid uri {}", uri),
            ));
        }
        match Uri::socket_path(&uri) {
            Some(path) => UnixStream::connect(path, &self.0).into_future(),
            _ => future::err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("Invalid uri {}", uri),
            )),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tokio_core::reactor::Core;

    #[test]
    fn connector_rejects_non_unix_uris() {
        let mut core = Core::new().unwrap();
        let connector = UnixConnector::new(core.handle());
        let work = connector.call("http://google.com".parse().unwrap());
        assert!(core.run(work).is_err())
    }

    #[test]
    fn connector_rejects_hand_crafted_unix_uris() {
        let mut core = Core::new().unwrap();
        let connector = UnixConnector::new(core.handle());
        let work = connector.call("unix://google.com".parse().unwrap());
        assert!(core.run(work).is_err())
    }
}<commit_msg>Implement clone for UnixConnector<commit_after>//! Hyper client bindings for unix domain sockets

use std::io;
use std::clone::Clone;

use futures::IntoFuture;
use futures::future::{self, FutureResult};
use hyper::Uri as HyperUri;
use tokio_core::reactor::Handle;
use tokio_service::Service;
use tokio_uds::UnixStream;

use super::Uri;

const UNIX_SCHEME: &str = "unix";

/// A type which implements hyper's client connector interface
/// for unix domain sockets
///
/// `UnixConnector` instances assume uri's
/// constructued with `hyperlocal::Uri::new()` which produce uris with a `unix://`
/// scheme
///
/// # examples
///
/// ```no_run
/// extern crate hyper;
/// extern crate hyperlocal;
/// extern crate tokio_core;
///
/// let core = tokio_core::reactor::Core::new().unwrap();
/// let client = hyper::Client::configure()
///    .connector(
///      hyperlocal::UnixConnector::new(core.handle())
///    )
///    .build(&core.handle());
/// ```
pub struct UnixConnector(Handle);

impl UnixConnector {
    pub fn new(handle: Handle) -> Self {
        UnixConnector(handle)
    }
}

impl Service for UnixConnector {
    type Request = HyperUri;
    type Response = UnixStream;
    type Error = io::Error;
    type Future = FutureResult<UnixStream, io::Error>;

    fn call(&self, uri: HyperUri) -> Self::Future {
        if uri.scheme() != Some(UNIX_SCHEME) {
            return future::err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("Invalid uri {}", uri),
            ));
        }
        match Uri::socket_path(&uri) {
            Some(path) => UnixStream::connect(path, &self.0).into_future(),
            _ => future::err(io::Error::new(
                io::ErrorKind::InvalidInput,
                format!("Invalid uri {}", uri),
            )),
        }
    }
}

impl Clone for UnixConnector {
    fn clone(&self) -> Self {
        UnixConnector(self.0.clone())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use tokio_core::reactor::Core;

    #[test]
    fn connector_rejects_non_unix_uris() {
        let mut core = Core::new().unwrap();
        let connector = UnixConnector::new(core.handle());
        let work = connector.call("http://google.com".parse().unwrap());
        assert!(core.run(work).is_err())
    }

    #[test]
    fn connector_rejects_hand_crafted_unix_uris() {
        let mut core = Core::new().unwrap();
        let connector = UnixConnector::new(core.handle());
        let work = connector.call("unix://google.com".parse().unwrap());
        assert!(core.run(work).is_err())
    }
}<|endoftext|>


<commit_before>//! HTTP Client

use std::cell::RefCell;
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::rc::Rc;
use std::time::Duration;

use futures::{future, Poll, Future, Stream};
#[cfg(feature = "compat")]
use http;
use tokio::reactor::Handle;
pub use tokio_service::Service;

use header::{Headers, Host};
use proto;
use proto::request;
use method::Method;
use self::pool::Pool;
use uri::{self, Uri};
use version::HttpVersion;

pub use proto::response::Response;
pub use proto::request::Request;
pub use self::connect::{HttpConnector, Connect};

mod connect;
mod dns;
mod pool;
#[cfg(feature = "compat")]
mod compat_impl;
#[cfg(feature = "compat")]
pub mod compat;

/// A Client to make outgoing HTTP requests.
// If the Connector is clone, then the Client can be clone easily.
pub struct Client<C, B = proto::Body> {
    connector: C,
    handle: Handle,
    pool: Pool<HyperClient<B>>,
}

impl Client<HttpConnector, proto::Body> {
    /// Create a new Client with the default config.
    #[inline]
    pub fn new(handle: &Handle) -> Client<HttpConnector, proto::Body> {
        Config::default().build(handle)
    }
}

impl Client<HttpConnector, proto::Body> {
    /// Configure a Client.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # extern crate hyper;
    /// # extern crate tokio_core;
    ///
    /// # fn main() {
    /// # let core = tokio_core::reactor::Core::new().unwrap();
    /// # let handle = core.handle();
    /// let client = hyper::Client::configure()
    ///     .keep_alive(true)
    ///     .build(&handle);
    /// # drop(client);
    /// # }
    /// ```
    #[inline]
    pub fn configure() -> Config<UseDefaultConnector, proto::Body> {
        Config::default()
    }
}

impl<C, B> Client<C, B> {
    /// Return a reference to a handle to the event loop this Client is associated with.
    #[inline]
    pub fn handle(&self) -> &Handle {
        &self.handle
    }

    /// Create a new client with a specific connector.
    #[inline]
    fn configured(config: Config<C, B>, handle: &Handle) -> Client<C, B> {
        Client {
            connector: config.connector,
            handle: handle.clone(),
            pool: Pool::new(config.keep_alive, config.keep_alive_timeout)
        }
    }
}

impl<C, B> Client<C, B>
where C: Connect,
      B: Stream<Error=::Error> + 'static,
      B::Item: AsRef<[u8]>,
{
    /// Send a GET Request using this Client.
    #[inline]
    pub fn get(&self, url: Uri) -> FutureResponse {
        self.request(Request::new(Method::Get, url))
    }

    /// Send a constructed Request using this Client.
    #[inline]
    pub fn request(&self, req: Request<B>) -> FutureResponse {
        self.call(req)
    }

    /// Send an `http::Request` using this Client.
    #[inline]
    #[cfg(feature = "compat")]
    pub fn request_compat(&self, req: http::Request<B>) -> compat::CompatFutureResponse {
        self::compat_impl::future(self.call(req.into()))
    }

    /// Convert into a client accepting `http::Request`.
    #[cfg(feature = "compat")]
    pub fn into_compat(self) -> compat::CompatClient<C, B> {
        self::compat_impl::client(self)
    }
}

/// A `Future` that will resolve to an HTTP Response.
#[must_use = "futures do nothing unless polled"]
pub struct FutureResponse(Box<Future<Item=Response, Error=::Error> + 'static>);

impl fmt::Debug for FutureResponse {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Future<Response>")
    }
}

impl Future for FutureResponse {
    type Item = Response;
    type Error = ::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.0.poll()
    }
}

impl<C, B> Service for Client<C, B>
where C: Connect,
      B: Stream<Error=::Error> + 'static,
      B::Item: AsRef<[u8]>,
{
    type Request = Request<B>;
    type Response = Response;
    type Error = ::Error;
    type Future = FutureResponse;

    fn call(&self, req: Self::Request) -> Self::Future {
        match req.version() {
            HttpVersion::Http10 |
            HttpVersion::Http11 => (),
            other => {
                error!("Request has unsupported version \"{}\"", other);
                return FutureResponse(Box::new(future::err(::Error::Version)));
            }
        }

        let url = req.uri().clone();
        let domain = match uri::scheme_and_authority(&url) {
            Some(uri) => uri,
            None => {
                return FutureResponse(Box::new(future::err(::Error::Io(
                    io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "invalid URI for Client Request"
                    )
                ))));
            }
        };
        let host = Host::new(domain.host().expect("authority implies host").to_owned(), domain.port());
        let (mut head, body) = request::split(req);
        let mut headers = Headers::new();
        headers.set(host);
        headers.extend(head.headers.iter());
        head.headers = headers;

        use futures::Sink;
        use futures::sync::{mpsc, oneshot};

        let checkout = self.pool.checkout(domain.as_ref());
        let connect = {
            let handle = self.handle.clone();
            let pool = self.pool.clone();
            let pool_key = Rc::new(domain.to_string());
            self.connector.connect(url)
                .map(move |io| {
                    let (tx, rx) = mpsc::channel(0);
                    let tx = HyperClient {
                        tx: RefCell::new(tx),
                        should_close: true,
                    };
                    let pooled = pool.pooled(pool_key, tx);
                    let conn = proto::Conn::<_, _, proto::ClientTransaction, _>::new(io, pooled.clone());
                    let dispatch = proto::dispatch::Dispatcher::new(proto::dispatch::Client::new(rx), conn);
                    handle.spawn(dispatch.map_err(|err| error!("client connection error: {}", err)));
                    pooled
                })
        };

        let race = checkout.select(connect)
            .map(|(client, _work)| client)
            .map_err(|(e, _work)| {
                // the Pool Checkout cannot error, so the only error
                // is from the Connector
                // XXX: should wait on the Checkout? Problem is
                // that if the connector is failing, it may be that we
                // never had a pooled stream at all
                e.into()
            });

        let resp = race.and_then(move |mut client| {
            let (callback, rx) = oneshot::channel();
            client.tx.borrow_mut().start_send(proto::dispatch::ClientMsg::Request(head, body, callback)).unwrap();
            client.should_close = false;
            rx.then(|res| {
                match res {
                    Ok(Ok(res)) => Ok(res),
                    Ok(Err(err)) => Err(err),
                    Err(_) => panic!("dispatch dropped without returning error"),
                }
            })
        });

        FutureResponse(Box::new(resp))
    }

}

impl<C: Clone, B> Clone for Client<C, B> {
    fn clone(&self) -> Client<C, B> {
        Client {
            connector: self.connector.clone(),
            handle: self.handle.clone(),
            pool: self.pool.clone(),
        }
    }
}

impl<C, B> fmt::Debug for Client<C, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Client")
    }
}

struct HyperClient<B> {
    tx: RefCell<::futures::sync::mpsc::Sender<proto::dispatch::ClientMsg<B>>>,
    should_close: bool,
}

impl<B> Clone for HyperClient<B> {
    fn clone(&self) -> HyperClient<B> {
        HyperClient {
            tx: self.tx.clone(),
            should_close: self.should_close,
        }
    }
}

impl<B> Drop for HyperClient<B> {
    fn drop(&mut self) {
        if self.should_close {
            self.should_close = false;
            let _ = self.tx.borrow_mut().try_send(proto::dispatch::ClientMsg::Close);
        }
    }
}

/// Configuration for a Client
pub struct Config<C, B> {
    _body_type: PhantomData<B>,
    //connect_timeout: Duration,
    connector: C,
    keep_alive: bool,
    keep_alive_timeout: Option<Duration>,
    //TODO: make use of max_idle config
    max_idle: usize,
    no_proto: bool,
}

/// Phantom type used to signal that `Config` should create a `HttpConnector`.
#[derive(Debug, Clone, Copy)]
pub struct UseDefaultConnector(());

impl Default for Config<UseDefaultConnector, proto::Body> {
    fn default() -> Config<UseDefaultConnector, proto::Body> {
        Config {
            _body_type: PhantomData::<proto::Body>,
            //connect_timeout: Duration::from_secs(10),
            connector: UseDefaultConnector(()),
            keep_alive: true,
            keep_alive_timeout: Some(Duration::from_secs(90)),
            max_idle: 5,
            no_proto: false,
        }
    }
}

impl<C, B> Config<C, B> {
    /// Set the body stream to be used by the `Client`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # use hyper::client::Config;
    /// let cfg = Config::default()
    ///     .body::<hyper::Body>();
    /// # drop(cfg);
    #[inline]
    pub fn body<BB>(self) -> Config<C, BB> {
        Config {
            _body_type: PhantomData::<BB>,
            //connect_timeout: self.connect_timeout,
            connector: self.connector,
            keep_alive: self.keep_alive,
            keep_alive_timeout: self.keep_alive_timeout,
            max_idle: self.max_idle,
            no_proto: self.no_proto,
        }
    }

    /// Set the `Connect` type to be used.
    #[inline]
    pub fn connector<CC>(self, val: CC) -> Config<CC, B> {
        Config {
            _body_type: self._body_type,
            //connect_timeout: self.connect_timeout,
            connector: val,
            keep_alive: self.keep_alive,
            keep_alive_timeout: self.keep_alive_timeout,
            max_idle: self.max_idle,
            no_proto: self.no_proto,
        }
    }

    /// Enable or disable keep-alive mechanics.
    ///
    /// Default is enabled.
    #[inline]
    pub fn keep_alive(mut self, val: bool) -> Config<C, B> {
        self.keep_alive = val;
        self
    }

    /// Set an optional timeout for idle sockets being kept-alive.
    ///
    /// Pass `None` to disable timeout.
    ///
    /// Default is 90 seconds.
    #[inline]
    pub fn keep_alive_timeout(mut self, val: Option<Duration>) -> Config<C, B> {
        self.keep_alive_timeout = val;
        self
    }

    /*
    /// Set the timeout for connecting to a URL.
    ///
    /// Default is 10 seconds.
    #[inline]
    pub fn connect_timeout(mut self, val: Duration) -> Config<C, B> {
        self.connect_timeout = val;
        self
    }
    */

    #[doc(hidden)]
    #[deprecated(since="0.11.11", note="no_proto is always enabled")]
    pub fn no_proto(self) -> Config<C, B> {
        self
    }
}

impl<C, B> Config<C, B>
where C: Connect,
      B: Stream<Error=::Error>,
      B::Item: AsRef<[u8]>,
{
    /// Construct the Client with this configuration.
    #[inline]
    pub fn build(self, handle: &Handle) -> Client<C, B> {
        Client::configured(self, handle)
    }
}

impl<B> Config<UseDefaultConnector, B>
where B: Stream<Error=::Error>,
      B::Item: AsRef<[u8]>,
{
    /// Construct the Client with this configuration.
    #[inline]
    pub fn build(self, handle: &Handle) -> Client<HttpConnector, B> {
        self.connector(HttpConnector::new(4, handle)).build(handle)
    }
}

impl<C, B> fmt::Debug for Config<C, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Config")
            .field("keep_alive", &self.keep_alive)
            .field("keep_alive_timeout", &self.keep_alive_timeout)
            .field("max_idle", &self.max_idle)
            .finish()
    }
}

impl<C: Clone, B> Clone for Config<C, B> {
    fn clone(&self) -> Config<C, B> {
        Config {
            connector: self.connector.clone(),
            .. *self
        }
    }
}
<commit_msg>fix(client): change connection errors to debug log level<commit_after>//! HTTP Client

use std::cell::RefCell;
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::rc::Rc;
use std::time::Duration;

use futures::{future, Poll, Future, Stream};
#[cfg(feature = "compat")]
use http;
use tokio::reactor::Handle;
pub use tokio_service::Service;

use header::{Headers, Host};
use proto;
use proto::request;
use method::Method;
use self::pool::Pool;
use uri::{self, Uri};
use version::HttpVersion;

pub use proto::response::Response;
pub use proto::request::Request;
pub use self::connect::{HttpConnector, Connect};

mod connect;
mod dns;
mod pool;
#[cfg(feature = "compat")]
mod compat_impl;
#[cfg(feature = "compat")]
pub mod compat;

/// A Client to make outgoing HTTP requests.
// If the Connector is clone, then the Client can be clone easily.
pub struct Client<C, B = proto::Body> {
    connector: C,
    handle: Handle,
    pool: Pool<HyperClient<B>>,
}

impl Client<HttpConnector, proto::Body> {
    /// Create a new Client with the default config.
    #[inline]
    pub fn new(handle: &Handle) -> Client<HttpConnector, proto::Body> {
        Config::default().build(handle)
    }
}

impl Client<HttpConnector, proto::Body> {
    /// Configure a Client.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # extern crate hyper;
    /// # extern crate tokio_core;
    ///
    /// # fn main() {
    /// # let core = tokio_core::reactor::Core::new().unwrap();
    /// # let handle = core.handle();
    /// let client = hyper::Client::configure()
    ///     .keep_alive(true)
    ///     .build(&handle);
    /// # drop(client);
    /// # }
    /// ```
    #[inline]
    pub fn configure() -> Config<UseDefaultConnector, proto::Body> {
        Config::default()
    }
}

impl<C, B> Client<C, B> {
    /// Return a reference to a handle to the event loop this Client is associated with.
    #[inline]
    pub fn handle(&self) -> &Handle {
        &self.handle
    }

    /// Create a new client with a specific connector.
    #[inline]
    fn configured(config: Config<C, B>, handle: &Handle) -> Client<C, B> {
        Client {
            connector: config.connector,
            handle: handle.clone(),
            pool: Pool::new(config.keep_alive, config.keep_alive_timeout)
        }
    }
}

impl<C, B> Client<C, B>
where C: Connect,
      B: Stream<Error=::Error> + 'static,
      B::Item: AsRef<[u8]>,
{
    /// Send a GET Request using this Client.
    #[inline]
    pub fn get(&self, url: Uri) -> FutureResponse {
        self.request(Request::new(Method::Get, url))
    }

    /// Send a constructed Request using this Client.
    #[inline]
    pub fn request(&self, req: Request<B>) -> FutureResponse {
        self.call(req)
    }

    /// Send an `http::Request` using this Client.
    #[inline]
    #[cfg(feature = "compat")]
    pub fn request_compat(&self, req: http::Request<B>) -> compat::CompatFutureResponse {
        self::compat_impl::future(self.call(req.into()))
    }

    /// Convert into a client accepting `http::Request`.
    #[cfg(feature = "compat")]
    pub fn into_compat(self) -> compat::CompatClient<C, B> {
        self::compat_impl::client(self)
    }
}

/// A `Future` that will resolve to an HTTP Response.
#[must_use = "futures do nothing unless polled"]
pub struct FutureResponse(Box<Future<Item=Response, Error=::Error> + 'static>);

impl fmt::Debug for FutureResponse {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Future<Response>")
    }
}

impl Future for FutureResponse {
    type Item = Response;
    type Error = ::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.0.poll()
    }
}

impl<C, B> Service for Client<C, B>
where C: Connect,
      B: Stream<Error=::Error> + 'static,
      B::Item: AsRef<[u8]>,
{
    type Request = Request<B>;
    type Response = Response;
    type Error = ::Error;
    type Future = FutureResponse;

    fn call(&self, req: Self::Request) -> Self::Future {
        match req.version() {
            HttpVersion::Http10 |
            HttpVersion::Http11 => (),
            other => {
                error!("Request has unsupported version \"{}\"", other);
                return FutureResponse(Box::new(future::err(::Error::Version)));
            }
        }

        let url = req.uri().clone();
        let domain = match uri::scheme_and_authority(&url) {
            Some(uri) => uri,
            None => {
                return FutureResponse(Box::new(future::err(::Error::Io(
                    io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "invalid URI for Client Request"
                    )
                ))));
            }
        };
        let host = Host::new(domain.host().expect("authority implies host").to_owned(), domain.port());
        let (mut head, body) = request::split(req);
        let mut headers = Headers::new();
        headers.set(host);
        headers.extend(head.headers.iter());
        head.headers = headers;

        use futures::Sink;
        use futures::sync::{mpsc, oneshot};

        let checkout = self.pool.checkout(domain.as_ref());
        let connect = {
            let handle = self.handle.clone();
            let pool = self.pool.clone();
            let pool_key = Rc::new(domain.to_string());
            self.connector.connect(url)
                .map(move |io| {
                    let (tx, rx) = mpsc::channel(0);
                    let tx = HyperClient {
                        tx: RefCell::new(tx),
                        should_close: true,
                    };
                    let pooled = pool.pooled(pool_key, tx);
                    let conn = proto::Conn::<_, _, proto::ClientTransaction, _>::new(io, pooled.clone());
                    let dispatch = proto::dispatch::Dispatcher::new(proto::dispatch::Client::new(rx), conn);
                    handle.spawn(dispatch.map_err(|err| debug!("client connection error: {}", err)));
                    pooled
                })
        };

        let race = checkout.select(connect)
            .map(|(client, _work)| client)
            .map_err(|(e, _work)| {
                // the Pool Checkout cannot error, so the only error
                // is from the Connector
                // XXX: should wait on the Checkout? Problem is
                // that if the connector is failing, it may be that we
                // never had a pooled stream at all
                e.into()
            });

        let resp = race.and_then(move |mut client| {
            let (callback, rx) = oneshot::channel();
            client.tx.borrow_mut().start_send(proto::dispatch::ClientMsg::Request(head, body, callback)).unwrap();
            client.should_close = false;
            rx.then(|res| {
                match res {
                    Ok(Ok(res)) => Ok(res),
                    Ok(Err(err)) => Err(err),
                    Err(_) => panic!("dispatch dropped without returning error"),
                }
            })
        });

        FutureResponse(Box::new(resp))
    }

}

impl<C: Clone, B> Clone for Client<C, B> {
    fn clone(&self) -> Client<C, B> {
        Client {
            connector: self.connector.clone(),
            handle: self.handle.clone(),
            pool: self.pool.clone(),
        }
    }
}

impl<C, B> fmt::Debug for Client<C, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("Client")
    }
}

struct HyperClient<B> {
    tx: RefCell<::futures::sync::mpsc::Sender<proto::dispatch::ClientMsg<B>>>,
    should_close: bool,
}

impl<B> Clone for HyperClient<B> {
    fn clone(&self) -> HyperClient<B> {
        HyperClient {
            tx: self.tx.clone(),
            should_close: self.should_close,
        }
    }
}

impl<B> Drop for HyperClient<B> {
    fn drop(&mut self) {
        if self.should_close {
            self.should_close = false;
            let _ = self.tx.borrow_mut().try_send(proto::dispatch::ClientMsg::Close);
        }
    }
}

/// Configuration for a Client
pub struct Config<C, B> {
    _body_type: PhantomData<B>,
    //connect_timeout: Duration,
    connector: C,
    keep_alive: bool,
    keep_alive_timeout: Option<Duration>,
    //TODO: make use of max_idle config
    max_idle: usize,
    no_proto: bool,
}

/// Phantom type used to signal that `Config` should create a `HttpConnector`.
#[derive(Debug, Clone, Copy)]
pub struct UseDefaultConnector(());

impl Default for Config<UseDefaultConnector, proto::Body> {
    fn default() -> Config<UseDefaultConnector, proto::Body> {
        Config {
            _body_type: PhantomData::<proto::Body>,
            //connect_timeout: Duration::from_secs(10),
            connector: UseDefaultConnector(()),
            keep_alive: true,
            keep_alive_timeout: Some(Duration::from_secs(90)),
            max_idle: 5,
            no_proto: false,
        }
    }
}

impl<C, B> Config<C, B> {
    /// Set the body stream to be used by the `Client`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # use hyper::client::Config;
    /// let cfg = Config::default()
    ///     .body::<hyper::Body>();
    /// # drop(cfg);
    #[inline]
    pub fn body<BB>(self) -> Config<C, BB> {
        Config {
            _body_type: PhantomData::<BB>,
            //connect_timeout: self.connect_timeout,
            connector: self.connector,
            keep_alive: self.keep_alive,
            keep_alive_timeout: self.keep_alive_timeout,
            max_idle: self.max_idle,
            no_proto: self.no_proto,
        }
    }

    /// Set the `Connect` type to be used.
    #[inline]
    pub fn connector<CC>(self, val: CC) -> Config<CC, B> {
        Config {
            _body_type: self._body_type,
            //connect_timeout: self.connect_timeout,
            connector: val,
            keep_alive: self.keep_alive,
            keep_alive_timeout: self.keep_alive_timeout,
            max_idle: self.max_idle,
            no_proto: self.no_proto,
        }
    }

    /// Enable or disable keep-alive mechanics.
    ///
    /// Default is enabled.
    #[inline]
    pub fn keep_alive(mut self, val: bool) -> Config<C, B> {
        self.keep_alive = val;
        self
    }

    /// Set an optional timeout for idle sockets being kept-alive.
    ///
    /// Pass `None` to disable timeout.
    ///
    /// Default is 90 seconds.
    #[inline]
    pub fn keep_alive_timeout(mut self, val: Option<Duration>) -> Config<C, B> {
        self.keep_alive_timeout = val;
        self
    }

    /*
    /// Set the timeout for connecting to a URL.
    ///
    /// Default is 10 seconds.
    #[inline]
    pub fn connect_timeout(mut self, val: Duration) -> Config<C, B> {
        self.connect_timeout = val;
        self
    }
    */

    #[doc(hidden)]
    #[deprecated(since="0.11.11", note="no_proto is always enabled")]
    pub fn no_proto(self) -> Config<C, B> {
        self
    }
}

impl<C, B> Config<C, B>
where C: Connect,
      B: Stream<Error=::Error>,
      B::Item: AsRef<[u8]>,
{
    /// Construct the Client with this configuration.
    #[inline]
    pub fn build(self, handle: &Handle) -> Client<C, B> {
        Client::configured(self, handle)
    }
}

impl<B> Config<UseDefaultConnector, B>
where B: Stream<Error=::Error>,
      B::Item: AsRef<[u8]>,
{
    /// Construct the Client with this configuration.
    #[inline]
    pub fn build(self, handle: &Handle) -> Client<HttpConnector, B> {
        self.connector(HttpConnector::new(4, handle)).build(handle)
    }
}

impl<C, B> fmt::Debug for Config<C, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Config")
            .field("keep_alive", &self.keep_alive)
            .field("keep_alive_timeout", &self.keep_alive_timeout)
            .field("max_idle", &self.max_idle)
            .finish()
    }
}

impl<C: Clone, B> Clone for Config<C, B> {
    fn clone(&self) -> Config<C, B> {
        Config {
            connector: self.connector.clone(),
            .. *self
        }
    }
}
<|endoftext|>