|
1 |
| -// Sync available crate categories from `src/categories.txt`. |
| 1 | +// Sync available crate categories from `src/categories.toml`. |
2 | 2 | // Runs when the server is started.
|
3 | 3 |
|
| 4 | +use toml; |
4 | 5 | use pg;
|
5 | 6 | use env;
|
6 |
| -use util::errors::CargoResult; |
| 7 | +use util::errors::{CargoResult, ChainError, internal}; |
| 8 | + |
| 9 | +struct Category { |
| 10 | + name: String, |
| 11 | + slug: String, |
| 12 | +} |
| 13 | + |
| 14 | +impl Category { |
| 15 | + fn concat(&self, child: &Category) -> Category { |
| 16 | + Category { |
| 17 | + name: format!("{}::{}", self.name, child.name), |
| 18 | + slug: format!("{}::{}", self.slug, child.slug), |
| 19 | + } |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +fn concat_parent_and_child(parent: Option<&Category>, child: Category) |
| 24 | + -> Category { |
| 25 | + parent.map(|p| p.concat(&child)).unwrap_or(child) |
| 26 | +} |
| 27 | + |
| 28 | +fn required_string_from_toml(toml: &toml::Table, key: &str) |
| 29 | + -> CargoResult<String> { |
| 30 | + toml.get(key) |
| 31 | + .and_then(toml::Value::as_str) |
| 32 | + .map(str::to_string) |
| 33 | + .chain_error(|| { |
| 34 | + internal("Expected Category toml attribute to be a String") |
| 35 | + }) |
| 36 | +} |
| 37 | + |
| 38 | +fn category_from_toml(toml: &toml::Value, parent: Option<&Category>) |
| 39 | + -> CargoResult<Vec<Category>> { |
| 40 | + let toml = toml.as_table().chain_error(|| { |
| 41 | + internal("Category isn't a toml Table") |
| 42 | + })?; |
| 43 | + |
| 44 | + let category = Category { |
| 45 | + slug: required_string_from_toml(&toml, "slug")?, |
| 46 | + name: required_string_from_toml(&toml, "name")?, |
| 47 | + }; |
| 48 | + |
| 49 | + let category = concat_parent_and_child(parent, category); |
| 50 | + |
| 51 | + let mut children: Vec<_> = toml.get("categories") |
| 52 | + .and_then(toml::Value::as_slice) |
| 53 | + .map(|children| { |
| 54 | + children.iter() |
| 55 | + .flat_map(|ref child| { |
| 56 | + category_from_toml(child, Some(&category)) |
| 57 | + .expect("Could not create child from toml") |
| 58 | + }).collect() |
| 59 | + }).unwrap_or(Vec::new()); |
| 60 | + |
| 61 | + children.push(category); |
| 62 | + |
| 63 | + Ok(children) |
| 64 | +} |
7 | 65 |
|
8 | 66 | pub fn sync() -> CargoResult<()> {
|
9 | 67 | let conn = pg::Connection::connect(&env("DATABASE_URL")[..],
|
10 | 68 | pg::TlsMode::None).unwrap();
|
11 | 69 | let tx = conn.transaction().unwrap();
|
12 | 70 |
|
13 |
| - let categories = include_str!("./categories.txt"); |
| 71 | + let categories = include_str!("./categories.toml"); |
| 72 | + let toml = toml::Parser::new(categories).parse().expect( |
| 73 | + "Could not parse categories.toml" |
| 74 | + ); |
| 75 | + |
| 76 | + let categories = toml.get("categories") |
| 77 | + .expect("No categories key found") |
| 78 | + .as_slice() |
| 79 | + .expect("Categories isn't a toml::Array"); |
14 | 80 |
|
15 |
| - let slug_categories: Vec<_> = categories.lines().map(|c| { |
16 |
| - let mut parts = c.splitn(2, ' '); |
17 |
| - let slug = parts.next().expect("No slug found!"); |
18 |
| - let name = parts.next().expect("No display name found!"); |
19 |
| - (slug, name) |
20 |
| - }).collect(); |
| 81 | + let categories: Vec<_> = categories |
| 82 | + .iter() |
| 83 | + .flat_map(|c| { |
| 84 | + category_from_toml(c, None) |
| 85 | + .expect("Categories from toml failed") |
| 86 | + }).collect(); |
21 | 87 |
|
22 |
| - let insert = slug_categories.iter().map(|&(ref slug, ref name)| { |
23 |
| - format!("(LOWER('{}'), '{}')", slug, name) |
| 88 | + let insert = categories.iter().map(|ref category| { |
| 89 | + format!("(LOWER('{}'), '{}')", category.slug, category.name) |
24 | 90 | }).collect::<Vec<_>>().join(",");
|
25 | 91 |
|
26 |
| - let in_clause = slug_categories.iter().map(|&(slug, _)| { |
27 |
| - format!("LOWER('{}')", slug) |
| 92 | + let in_clause = categories.iter().map(|ref category| { |
| 93 | + format!("LOWER('{}')", category.slug) |
28 | 94 | }).collect::<Vec<_>>().join(",");
|
29 | 95 |
|
30 | 96 | try!(tx.batch_execute(
|
|
0 commit comments