<commit_before>use serde::Deserialize;
use std::collections::BTreeMap;
use std::path::Path;

use std::collections::BTreeSet as Set;
use std::fs;

/// A lookup table from TypeName to Entry.
pub type NodeTypeMap = BTreeMap<TypeName, Entry>;

#[derive(Debug)]
pub struct Entry {
    pub dbscheme_name: String,
    pub ql_class_name: String,
    pub kind: EntryKind,
}

#[derive(Debug)]
pub enum EntryKind {
    Union { members: Set<TypeName> },
    Table { name: String, fields: Vec<Field> },
    Token { kind_id: usize },
}

#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct TypeName {
    pub kind: String,
    pub named: bool,
}

#[derive(Debug)]
pub enum FieldTypeInfo {
    /// The field has a single type.
    Single(TypeName),

    /// The field can take one of several types, so we also provide the name of
    /// the database union type that wraps them, and the corresponding QL class
    /// name.
    Multiple {
        types: Set<TypeName>,
        dbscheme_union: String,
        ql_class: String,
    },

    /// The field can be one of several tokens, so the db type will be an `int`
    /// with a `case @foo.kind` for each possiblity.
    ReservedWordInt(BTreeMap<String, (usize, String)>),
}

#[derive(Debug)]
pub struct Field {
    pub parent: TypeName,
    pub type_info: FieldTypeInfo,
    /// The name of the field or None for the anonymous 'children'
    /// entry from node_types.json
    pub name: Option<String>,
    /// The name of the predicate to get this field.
    pub getter_name: String,
    pub storage: Storage,
}

fn name_for_field_or_child(name: &Option<String>) -> String {
    match name {
        Some(name) => name.clone(),
        None => "child".to_owned(),
    }
}

#[derive(Debug)]
pub enum Storage {
    /// the field is stored as a column in the parent table
    Column { name: String },
    /// the field is stored in a link table
    Table {
        /// the name of the table
        name: String,
        /// the name of the column for the field in the dbscheme
        column_name: String,
        /// does it have an associated index column?
        has_index: bool,
    },
}

pub fn read_node_types(prefix: &str, node_types_path: &Path) -> std::io::Result<NodeTypeMap> {
    let file = fs::File::open(node_types_path)?;
    let node_types = serde_json::from_reader(file)?;
    Ok(convert_nodes(&prefix, &node_types))
}

pub fn read_node_types_str(prefix: &str, node_types_json: &str) -> std::io::Result<NodeTypeMap> {
    let node_types = serde_json::from_str(node_types_json)?;
    Ok(convert_nodes(&prefix, &node_types))
}

fn convert_type(node_type: &NodeType) -> TypeName {
    TypeName {
        kind: node_type.kind.to_string(),
        named: node_type.named,
    }
}

fn convert_types(node_types: &Vec<NodeType>) -> Set<TypeName> {
    let iter = node_types.iter().map(convert_type).collect();
    std::collections::BTreeSet::from(iter)
}

pub fn convert_nodes(prefix: &str, nodes: &Vec<NodeInfo>) -> NodeTypeMap {
    let mut entries = NodeTypeMap::new();
    let mut token_kinds = Set::new();

    // First, find all the token kinds
    for node in nodes {
        if node.subtypes.is_none() {
            if node.fields.as_ref().map_or(0, |x| x.len()) == 0 && node.children.is_none() {
                let type_name = TypeName {
                    kind: node.kind.clone(),
                    named: node.named,
                };
                token_kinds.insert(type_name);
            }
        }
    }

    for node in nodes {
        let flattened_name = &node_type_name(&node.kind, node.named);
        let dbscheme_name = escape_name(&flattened_name);
        let ql_class_name = dbscheme_name_to_class_name(&dbscheme_name);
        let dbscheme_name = format!("{}_{}", prefix, &dbscheme_name);
        if let Some(subtypes) = &node.subtypes {
            // It's a tree-sitter supertype node, for which we create a union
            // type.
            entries.insert(
                TypeName {
                    kind: node.kind.clone(),
                    named: node.named,
                },
                Entry {
                    dbscheme_name,
                    ql_class_name,
                    kind: EntryKind::Union {
                        members: convert_types(&subtypes),
                    },
                },
            );
        } else if node.fields.as_ref().map_or(0, |x| x.len()) == 0 && node.children.is_none() {
            // Token kind, handled above.
        } else {
            // It's a product type, defined by a table.
            let type_name = TypeName {
                kind: node.kind.clone(),
                named: node.named,
            };
            let table_name = escape_name(&(format!("{}_def", &flattened_name)));
            let table_name = format!("{}_{}", prefix, &table_name);

            let mut fields = Vec::new();

            // If the type also has fields or children, then we create either
            // auxiliary tables or columns in the defining table for them.
            if let Some(node_fields) = &node.fields {
                for (field_name, field_info) in node_fields {
                    add_field(
                        &prefix,
                        &type_name,
                        Some(field_name.to_string()),
                        field_info,
                        &mut fields,
                        &token_kinds,
                    );
                }
            }
            if let Some(children) = &node.children {
                // Treat children as if they were a field called 'child'.
                add_field(
                    &prefix,
                    &type_name,
                    None,
                    children,
                    &mut fields,
                    &token_kinds,
                );
            }
            entries.insert(
                type_name,
                Entry {
                    dbscheme_name,
                    ql_class_name,
                    kind: EntryKind::Table {
                        name: table_name,
                        fields,
                    },
                },
            );
        }
    }
    let mut counter = 0;
    for type_name in token_kinds {
        let entry = if type_name.named {
            counter += 1;
            let unprefixed_name = node_type_name(&type_name.kind, true);
            Entry {
                dbscheme_name: escape_name(&format!("{}_token_{}", &prefix, &unprefixed_name)),
                ql_class_name: dbscheme_name_to_class_name(&escape_name(&unprefixed_name)),
                kind: EntryKind::Token { kind_id: counter },
            }
        } else {
            Entry {
                dbscheme_name: format!("{}_reserved_word", &prefix),
                ql_class_name: "ReservedWord".to_owned(),
                kind: EntryKind::Token { kind_id: 0 },
            }
        };
        entries.insert(type_name, entry);
    }
    entries
}

fn add_field(
    prefix: &str,
    parent_type_name: &TypeName,
    field_name: Option<String>,
    field_info: &FieldInfo,
    fields: &mut Vec<Field>,
    token_kinds: &Set<TypeName>,
) {
    let parent_flattened_name = node_type_name(&parent_type_name.kind, parent_type_name.named);
    let column_name = escape_name(&name_for_field_or_child(&field_name));
    let storage = if !field_info.multiple && field_info.required {
        // This field must appear exactly once, so we add it as
        // a column to the main table for the node type.
        Storage::Column { name: column_name }
    } else {
        // Put the field in an auxiliary table.
        let has_index = field_info.multiple;
        let field_table_name = escape_name(&format!(
            "{}_{}_{}",
            &prefix,
            parent_flattened_name,
            &name_for_field_or_child(&field_name)
        ));
        Storage::Table {
            has_index,
            name: field_table_name,
            column_name,
        }
    };
    let converted_types = convert_types(&field_info.types);
    let type_info = if field_info
        .types
        .iter()
        .all(|t| !t.named && token_kinds.contains(&convert_type(t)))
    {
        // All possible types for this field are reserved words. The db
        // representation will be an `int` with a `case @foo.field = ...` to
        // enumerate the possible values.
        let mut counter = 0;
        let mut field_token_ints: BTreeMap<String, (usize, String)> = BTreeMap::new();
        for t in converted_types {
            let dbscheme_variant_name =
                escape_name(&format!("{}_{}_{}", &prefix, parent_flattened_name, t.kind));
            field_token_ints.insert(t.kind.to_owned(), (counter, dbscheme_variant_name));
            counter += 1;
        }
        FieldTypeInfo::ReservedWordInt(field_token_ints)
    } else if field_info.types.len() == 1 {
        FieldTypeInfo::Single(converted_types.into_iter().next().unwrap())
    } else {
        // The dbscheme type for this field will be a union. In QL, it'll just be AstNode.
        FieldTypeInfo::Multiple {
            types: converted_types,
            dbscheme_union: format!(
                "{}_{}_{}_type",
                &prefix,
                &parent_flattened_name,
                &name_for_field_or_child(&field_name)
            ),
            ql_class: "AstNode".to_owned(),
        }
    };
    let getter_name = format!(
        "get{}",
        dbscheme_name_to_class_name(&escape_name(&name_for_field_or_child(&field_name)))
    );
    fields.push(Field {
        parent: TypeName {
            kind: parent_type_name.kind.to_string(),
            named: parent_type_name.named,
        },
        type_info,
        name: field_name,
        getter_name,
        storage,
    });
}
#[derive(Deserialize)]
pub struct NodeInfo {
    #[serde(rename = "type")]
    pub kind: String,
    pub named: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fields: Option<BTreeMap<String, FieldInfo>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub children: Option<FieldInfo>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subtypes: Option<Vec<NodeType>>,
}

#[derive(Deserialize)]
pub struct NodeType {
    #[serde(rename = "type")]
    pub kind: String,
    pub named: bool,
}

#[derive(Deserialize)]
pub struct FieldInfo {
    pub multiple: bool,
    pub required: bool,
    pub types: Vec<NodeType>,
}

/// Given a tree-sitter node type's (kind, named) pair, returns a single string
/// representing the (unescaped) name we'll use to refer to corresponding QL
/// type.
fn node_type_name(kind: &str, named: bool) -> String {
    if named {
        kind.to_string()
    } else {
        format!("{}_unnamed", kind)
    }
}

const RESERVED_KEYWORDS: [&'static str; 14] = [
    "boolean", "case", "date", "float", "int", "key", "of", "order", "ref", "string", "subtype",
    "type", "unique", "varchar",
];

/// Returns a string that's a copy of `name` but suitably escaped to be a valid
/// QL identifier.
fn escape_name(name: &str) -> String {
    let mut result = String::new();

    // If there's a leading underscore, replace it with 'underscore_'.
    if let Some(c) = name.chars().next() {
        if c == '_' {
            result.push_str("underscore");
        }
    }
    for c in name.chars() {
        match c {
            '{' => result.push_str("lbrace"),
            '}' => result.push_str("rbrace"),
            '<' => result.push_str("langle"),
            '>' => result.push_str("rangle"),
            '[' => result.push_str("lbracket"),
            ']' => result.push_str("rbracket"),
            '(' => result.push_str("lparen"),
            ')' => result.push_str("rparen"),
            '|' => result.push_str("pipe"),
            '=' => result.push_str("equal"),
            '~' => result.push_str("tilde"),
            '?' => result.push_str("question"),
            '`' => result.push_str("backtick"),
            '^' => result.push_str("caret"),
            '!' => result.push_str("bang"),
            '#' => result.push_str("hash"),
            '%' => result.push_str("percent"),
            '&' => result.push_str("ampersand"),
            '.' => result.push_str("dot"),
            ',' => result.push_str("comma"),
            '/' => result.push_str("slash"),
            ':' => result.push_str("colon"),
            ';' => result.push_str("semicolon"),
            '"' => result.push_str("dquote"),
            '*' => result.push_str("star"),
            '+' => result.push_str("plus"),
            '-' => result.push_str("minus"),
            '@' => result.push_str("at"),
            _ if c.is_uppercase() => {
                result.push('_');
                result.push_str(&c.to_lowercase().to_string())
            }
            _ => result.push(c),
        }
    }

    for &keyword in &RESERVED_KEYWORDS {
        if result == keyword {
            result.push_str("__");
            break;
        }
    }

    result
}

pub fn to_snake_case(word: &str) -> String {
    let mut prev_upper = true;
    let mut result = String::new();
    for c in word.chars() {
        if c.is_uppercase() {
            if !prev_upper {
                result.push('_')
            }
            prev_upper = true;
            result.push(c.to_ascii_lowercase());
        } else {
            prev_upper = false;
            result.push(c);
        }
    }
    result
}
/// Given a valid dbscheme name (i.e. in snake case), produces the equivalent QL
/// name (i.e. in CamelCase). For example, "foo_bar_baz" becomes "FooBarBaz".
fn dbscheme_name_to_class_name(dbscheme_name: &str) -> String {
    fn to_title_case(word: &str) -> String {
        let mut first = true;
        let mut result = String::new();
        for c in word.chars() {
            if first {
                first = false;
                result.push(c.to_ascii_uppercase());
            } else {
                result.push(c);
            }
        }
        result
    }
    dbscheme_name
        .split('_')
        .map(|word| to_title_case(word))
        .collect::<Vec<String>>()
        .join("")
}

#[test]
fn to_snake_case_test() {
    assert_eq!("ruby", to_snake_case("Ruby"));
    assert_eq!("erb", to_snake_case("ERB"));
    assert_eq!("embedded_template", to_snake_case("EmbeddedTemplate"));
}
<commit_msg>Fix clippy warnings in node-types<commit_after>use serde::Deserialize;
use std::collections::BTreeMap;
use std::path::Path;

use std::collections::BTreeSet as Set;
use std::fs;

/// A lookup table from TypeName to Entry.
pub type NodeTypeMap = BTreeMap<TypeName, Entry>;

#[derive(Debug)]
pub struct Entry {
    pub dbscheme_name: String,
    pub ql_class_name: String,
    pub kind: EntryKind,
}

#[derive(Debug)]
pub enum EntryKind {
    Union { members: Set<TypeName> },
    Table { name: String, fields: Vec<Field> },
    Token { kind_id: usize },
}

#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
pub struct TypeName {
    pub kind: String,
    pub named: bool,
}

#[derive(Debug)]
pub enum FieldTypeInfo {
    /// The field has a single type.
    Single(TypeName),

    /// The field can take one of several types, so we also provide the name of
    /// the database union type that wraps them, and the corresponding QL class
    /// name.
    Multiple {
        types: Set<TypeName>,
        dbscheme_union: String,
        ql_class: String,
    },

    /// The field can be one of several tokens, so the db type will be an `int`
    /// with a `case @foo.kind` for each possiblity.
    ReservedWordInt(BTreeMap<String, (usize, String)>),
}

#[derive(Debug)]
pub struct Field {
    pub parent: TypeName,
    pub type_info: FieldTypeInfo,
    /// The name of the field or None for the anonymous 'children'
    /// entry from node_types.json
    pub name: Option<String>,
    /// The name of the predicate to get this field.
    pub getter_name: String,
    pub storage: Storage,
}

fn name_for_field_or_child(name: &Option<String>) -> String {
    match name {
        Some(name) => name.clone(),
        None => "child".to_owned(),
    }
}

#[derive(Debug)]
pub enum Storage {
    /// the field is stored as a column in the parent table
    Column { name: String },
    /// the field is stored in a link table
    Table {
        /// the name of the table
        name: String,
        /// the name of the column for the field in the dbscheme
        column_name: String,
        /// does it have an associated index column?
        has_index: bool,
    },
}

pub fn read_node_types(prefix: &str, node_types_path: &Path) -> std::io::Result<NodeTypeMap> {
    let file = fs::File::open(node_types_path)?;
    let node_types: Vec<NodeInfo> = serde_json::from_reader(file)?;
    Ok(convert_nodes(prefix, &node_types))
}

pub fn read_node_types_str(prefix: &str, node_types_json: &str) -> std::io::Result<NodeTypeMap> {
    let node_types: Vec<NodeInfo> = serde_json::from_str(node_types_json)?;
    Ok(convert_nodes(prefix, &node_types))
}

fn convert_type(node_type: &NodeType) -> TypeName {
    TypeName {
        kind: node_type.kind.to_string(),
        named: node_type.named,
    }
}

fn convert_types(node_types: &[NodeType]) -> Set<TypeName> {
    node_types.iter().map(convert_type).collect()
}

pub fn convert_nodes(prefix: &str, nodes: &[NodeInfo]) -> NodeTypeMap {
    let mut entries = NodeTypeMap::new();
    let mut token_kinds = Set::new();

    // First, find all the token kinds
    for node in nodes {
        if node.subtypes.is_none()
            && node.fields.as_ref().map_or(0, |x| x.len()) == 0
            && node.children.is_none()
        {
            let type_name = TypeName {
                kind: node.kind.clone(),
                named: node.named,
            };
            token_kinds.insert(type_name);
        }
    }

    for node in nodes {
        let flattened_name = &node_type_name(&node.kind, node.named);
        let dbscheme_name = escape_name(flattened_name);
        let ql_class_name = dbscheme_name_to_class_name(&dbscheme_name);
        let dbscheme_name = format!("{}_{}", prefix, &dbscheme_name);
        if let Some(subtypes) = &node.subtypes {
            // It's a tree-sitter supertype node, for which we create a union
            // type.
            entries.insert(
                TypeName {
                    kind: node.kind.clone(),
                    named: node.named,
                },
                Entry {
                    dbscheme_name,
                    ql_class_name,
                    kind: EntryKind::Union {
                        members: convert_types(subtypes),
                    },
                },
            );
        } else if node.fields.as_ref().map_or(0, |x| x.len()) == 0 && node.children.is_none() {
            // Token kind, handled above.
        } else {
            // It's a product type, defined by a table.
            let type_name = TypeName {
                kind: node.kind.clone(),
                named: node.named,
            };
            let table_name = escape_name(&(format!("{}_def", &flattened_name)));
            let table_name = format!("{}_{}", prefix, &table_name);

            let mut fields = Vec::new();

            // If the type also has fields or children, then we create either
            // auxiliary tables or columns in the defining table for them.
            if let Some(node_fields) = &node.fields {
                for (field_name, field_info) in node_fields {
                    add_field(
                        prefix,
                        &type_name,
                        Some(field_name.to_string()),
                        field_info,
                        &mut fields,
                        &token_kinds,
                    );
                }
            }
            if let Some(children) = &node.children {
                // Treat children as if they were a field called 'child'.
                add_field(
                    prefix,
                    &type_name,
                    None,
                    children,
                    &mut fields,
                    &token_kinds,
                );
            }
            entries.insert(
                type_name,
                Entry {
                    dbscheme_name,
                    ql_class_name,
                    kind: EntryKind::Table {
                        name: table_name,
                        fields,
                    },
                },
            );
        }
    }
    let mut counter = 0;
    for type_name in token_kinds {
        let entry = if type_name.named {
            counter += 1;
            let unprefixed_name = node_type_name(&type_name.kind, true);
            Entry {
                dbscheme_name: escape_name(&format!("{}_token_{}", &prefix, &unprefixed_name)),
                ql_class_name: dbscheme_name_to_class_name(&escape_name(&unprefixed_name)),
                kind: EntryKind::Token { kind_id: counter },
            }
        } else {
            Entry {
                dbscheme_name: format!("{}_reserved_word", &prefix),
                ql_class_name: "ReservedWord".to_owned(),
                kind: EntryKind::Token { kind_id: 0 },
            }
        };
        entries.insert(type_name, entry);
    }
    entries
}

fn add_field(
    prefix: &str,
    parent_type_name: &TypeName,
    field_name: Option<String>,
    field_info: &FieldInfo,
    fields: &mut Vec<Field>,
    token_kinds: &Set<TypeName>,
) {
    let parent_flattened_name = node_type_name(&parent_type_name.kind, parent_type_name.named);
    let column_name = escape_name(&name_for_field_or_child(&field_name));
    let storage = if !field_info.multiple && field_info.required {
        // This field must appear exactly once, so we add it as
        // a column to the main table for the node type.
        Storage::Column { name: column_name }
    } else {
        // Put the field in an auxiliary table.
        let has_index = field_info.multiple;
        let field_table_name = escape_name(&format!(
            "{}_{}_{}",
            &prefix,
            parent_flattened_name,
            &name_for_field_or_child(&field_name)
        ));
        Storage::Table {
            has_index,
            name: field_table_name,
            column_name,
        }
    };
    let converted_types = convert_types(&field_info.types);
    let type_info = if field_info
        .types
        .iter()
        .all(|t| !t.named && token_kinds.contains(&convert_type(t)))
    {
        // All possible types for this field are reserved words. The db
        // representation will be an `int` with a `case @foo.field = ...` to
        // enumerate the possible values.
        let mut field_token_ints: BTreeMap<String, (usize, String)> = BTreeMap::new();
        for (counter, t) in converted_types.into_iter().enumerate() {
            let dbscheme_variant_name =
                escape_name(&format!("{}_{}_{}", &prefix, parent_flattened_name, t.kind));
            field_token_ints.insert(t.kind.to_owned(), (counter, dbscheme_variant_name));
        }
        FieldTypeInfo::ReservedWordInt(field_token_ints)
    } else if field_info.types.len() == 1 {
        FieldTypeInfo::Single(converted_types.into_iter().next().unwrap())
    } else {
        // The dbscheme type for this field will be a union. In QL, it'll just be AstNode.
        FieldTypeInfo::Multiple {
            types: converted_types,
            dbscheme_union: format!(
                "{}_{}_{}_type",
                &prefix,
                &parent_flattened_name,
                &name_for_field_or_child(&field_name)
            ),
            ql_class: "AstNode".to_owned(),
        }
    };
    let getter_name = format!(
        "get{}",
        dbscheme_name_to_class_name(&escape_name(&name_for_field_or_child(&field_name)))
    );
    fields.push(Field {
        parent: TypeName {
            kind: parent_type_name.kind.to_string(),
            named: parent_type_name.named,
        },
        type_info,
        name: field_name,
        getter_name,
        storage,
    });
}
#[derive(Deserialize)]
pub struct NodeInfo {
    #[serde(rename = "type")]
    pub kind: String,
    pub named: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub fields: Option<BTreeMap<String, FieldInfo>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub children: Option<FieldInfo>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub subtypes: Option<Vec<NodeType>>,
}

#[derive(Deserialize)]
pub struct NodeType {
    #[serde(rename = "type")]
    pub kind: String,
    pub named: bool,
}

#[derive(Deserialize)]
pub struct FieldInfo {
    pub multiple: bool,
    pub required: bool,
    pub types: Vec<NodeType>,
}

/// Given a tree-sitter node type's (kind, named) pair, returns a single string
/// representing the (unescaped) name we'll use to refer to corresponding QL
/// type.
fn node_type_name(kind: &str, named: bool) -> String {
    if named {
        kind.to_string()
    } else {
        format!("{}_unnamed", kind)
    }
}

const RESERVED_KEYWORDS: [&str; 14] = [
    "boolean", "case", "date", "float", "int", "key", "of", "order", "ref", "string", "subtype",
    "type", "unique", "varchar",
];

/// Returns a string that's a copy of `name` but suitably escaped to be a valid
/// QL identifier.
fn escape_name(name: &str) -> String {
    let mut result = String::new();

    // If there's a leading underscore, replace it with 'underscore_'.
    if let Some(c) = name.chars().next() {
        if c == '_' {
            result.push_str("underscore");
        }
    }
    for c in name.chars() {
        match c {
            '{' => result.push_str("lbrace"),
            '}' => result.push_str("rbrace"),
            '<' => result.push_str("langle"),
            '>' => result.push_str("rangle"),
            '[' => result.push_str("lbracket"),
            ']' => result.push_str("rbracket"),
            '(' => result.push_str("lparen"),
            ')' => result.push_str("rparen"),
            '|' => result.push_str("pipe"),
            '=' => result.push_str("equal"),
            '~' => result.push_str("tilde"),
            '?' => result.push_str("question"),
            '`' => result.push_str("backtick"),
            '^' => result.push_str("caret"),
            '!' => result.push_str("bang"),
            '#' => result.push_str("hash"),
            '%' => result.push_str("percent"),
            '&' => result.push_str("ampersand"),
            '.' => result.push_str("dot"),
            ',' => result.push_str("comma"),
            '/' => result.push_str("slash"),
            ':' => result.push_str("colon"),
            ';' => result.push_str("semicolon"),
            '"' => result.push_str("dquote"),
            '*' => result.push_str("star"),
            '+' => result.push_str("plus"),
            '-' => result.push_str("minus"),
            '@' => result.push_str("at"),
            _ if c.is_uppercase() => {
                result.push('_');
                result.push_str(&c.to_lowercase().to_string())
            }
            _ => result.push(c),
        }
    }

    for &keyword in &RESERVED_KEYWORDS {
        if result == keyword {
            result.push_str("__");
            break;
        }
    }

    result
}

pub fn to_snake_case(word: &str) -> String {
    let mut prev_upper = true;
    let mut result = String::new();
    for c in word.chars() {
        if c.is_uppercase() {
            if !prev_upper {
                result.push('_')
            }
            prev_upper = true;
            result.push(c.to_ascii_lowercase());
        } else {
            prev_upper = false;
            result.push(c);
        }
    }
    result
}
/// Given a valid dbscheme name (i.e. in snake case), produces the equivalent QL
/// name (i.e. in CamelCase). For example, "foo_bar_baz" becomes "FooBarBaz".
fn dbscheme_name_to_class_name(dbscheme_name: &str) -> String {
    fn to_title_case(word: &str) -> String {
        let mut first = true;
        let mut result = String::new();
        for c in word.chars() {
            if first {
                first = false;
                result.push(c.to_ascii_uppercase());
            } else {
                result.push(c);
            }
        }
        result
    }
    dbscheme_name
        .split('_')
        .map(|word| to_title_case(word))
        .collect::<Vec<String>>()
        .join("")
}

#[test]
fn to_snake_case_test() {
    assert_eq!("ruby", to_snake_case("Ruby"));
    assert_eq!("erb", to_snake_case("ERB"));
    assert_eq!("embedded_template", to_snake_case("EmbeddedTemplate"));
}
<|endoftext|>


<commit_before>use crate::def::{CtorOf, DefKind, Res};
use crate::def_id::DefId;
use crate::hir::{self, HirId, PatKind};
use rustc_span::symbol::Ident;
use rustc_span::Span;

use std::iter::{Enumerate, ExactSizeIterator};

pub struct EnumerateAndAdjust<I> {
    enumerate: Enumerate<I>,
    gap_pos: usize,
    gap_len: usize,
}

impl<I> Iterator for EnumerateAndAdjust<I>
where
    I: Iterator,
{
    type Item = (usize, <I as Iterator>::Item);

    fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
        self.enumerate
            .next()
            .map(|(i, elem)| (if i < self.gap_pos { i } else { i + self.gap_len }, elem))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.enumerate.size_hint()
    }
}

pub trait EnumerateAndAdjustIterator {
    fn enumerate_and_adjust(
        self,
        expected_len: usize,
        gap_pos: Option<usize>,
    ) -> EnumerateAndAdjust<Self>
    where
        Self: Sized;
}

impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
    fn enumerate_and_adjust(
        self,
        expected_len: usize,
        gap_pos: Option<usize>,
    ) -> EnumerateAndAdjust<Self>
    where
        Self: Sized,
    {
        let actual_len = self.len();
        EnumerateAndAdjust {
            enumerate: self.enumerate(),
            gap_pos: gap_pos.unwrap_or(expected_len),
            gap_len: expected_len - actual_len,
        }
    }
}

impl hir::Pat<'_> {
    /// Call `f` on every "binding" in a pattern, e.g., on `a` in
    /// `match foo() { Some(a) => (), None => () }`
    pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) {
        self.walk_always(|p| {
            if let PatKind::Binding(binding_mode, _, ident, _) = p.kind {
                f(binding_mode, p.hir_id, p.span, ident);
            }
        });
    }

    /// Call `f` on every "binding" in a pattern, e.g., on `a` in
    /// `match foo() { Some(a) => (), None => () }`.
    ///
    /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited.
    pub fn each_binding_or_first(
        &self,
        f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident),
    ) {
        self.walk(|p| match &p.kind {
            PatKind::Or(ps) => {
                ps[0].each_binding_or_first(f);
                false
            }
            PatKind::Binding(bm, _, ident, _) => {
                f(*bm, p.hir_id, p.span, *ident);
                true
            }
            _ => true,
        })
    }

    /// Checks if the pattern contains any patterns that bind something to
    /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
    pub fn contains_bindings(&self) -> bool {
        self.satisfies(|p| matches!(p.kind, PatKind::Binding(..)))
    }

    /// Checks if the pattern satisfies the given predicate on some sub-pattern.
    fn satisfies(&self, pred: impl Fn(&hir::Pat<'_>) -> bool) -> bool {
        let mut satisfies = false;
        self.walk_short(|p| {
            if pred(p) {
                satisfies = true;
                false // Found one, can short circuit now.
            } else {
                true
            }
        });
        satisfies
    }

    pub fn simple_ident(&self) -> Option<Ident> {
        match self.kind {
            PatKind::Binding(
                hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
                _,
                ident,
                None,
            ) => Some(ident),
            _ => None,
        }
    }

    /// Returns variants that are necessary to exist for the pattern to match.
    pub fn necessary_variants(&self) -> Vec<DefId> {
        let mut variants = vec![];
        self.walk(|p| match &p.kind {
            PatKind::Or(_) => false,
            PatKind::Path(hir::QPath::Resolved(_, path))
            | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..)
            | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => {
                if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) =
                    path.res
                {
                    variants.push(id);
                }
                true
            }
            _ => true,
        });
        variants.sort();
        variants.dedup();
        variants
    }

    /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if
    /// yes whether it contains mutable or just immutables ones.
    //
    // FIXME(tschottdorf): this is problematic as the HIR is being scraped, but
    // ref bindings are be implicit after #42640 (default match binding modes). See issue #44848.
    pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
        let mut result = None;
        self.each_binding(|annotation, _, _, _| match annotation {
            hir::BindingAnnotation::Ref => match result {
                None | Some(hir::Mutability::Not) => result = Some(hir::Mutability::Not),
                _ => {}
            },
            hir::BindingAnnotation::RefMut => result = Some(hir::Mutability::Mut),
            _ => {}
        });
        result
    }
}
<commit_msg>Avoid sorting by DefId for `necessary_variants()`<commit_after>use crate::def::{CtorOf, DefKind, Res};
use crate::def_id::DefId;
use crate::hir::{self, HirId, PatKind};
use rustc_data_structures::stable_set::FxHashSet;
use rustc_span::symbol::Ident;
use rustc_span::Span;

use std::iter::{Enumerate, ExactSizeIterator};

pub struct EnumerateAndAdjust<I> {
    enumerate: Enumerate<I>,
    gap_pos: usize,
    gap_len: usize,
}

impl<I> Iterator for EnumerateAndAdjust<I>
where
    I: Iterator,
{
    type Item = (usize, <I as Iterator>::Item);

    fn next(&mut self) -> Option<(usize, <I as Iterator>::Item)> {
        self.enumerate
            .next()
            .map(|(i, elem)| (if i < self.gap_pos { i } else { i + self.gap_len }, elem))
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        self.enumerate.size_hint()
    }
}

pub trait EnumerateAndAdjustIterator {
    fn enumerate_and_adjust(
        self,
        expected_len: usize,
        gap_pos: Option<usize>,
    ) -> EnumerateAndAdjust<Self>
    where
        Self: Sized;
}

impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
    fn enumerate_and_adjust(
        self,
        expected_len: usize,
        gap_pos: Option<usize>,
    ) -> EnumerateAndAdjust<Self>
    where
        Self: Sized,
    {
        let actual_len = self.len();
        EnumerateAndAdjust {
            enumerate: self.enumerate(),
            gap_pos: gap_pos.unwrap_or(expected_len),
            gap_len: expected_len - actual_len,
        }
    }
}

impl hir::Pat<'_> {
    /// Call `f` on every "binding" in a pattern, e.g., on `a` in
    /// `match foo() { Some(a) => (), None => () }`
    pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) {
        self.walk_always(|p| {
            if let PatKind::Binding(binding_mode, _, ident, _) = p.kind {
                f(binding_mode, p.hir_id, p.span, ident);
            }
        });
    }

    /// Call `f` on every "binding" in a pattern, e.g., on `a` in
    /// `match foo() { Some(a) => (), None => () }`.
    ///
    /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited.
    pub fn each_binding_or_first(
        &self,
        f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident),
    ) {
        self.walk(|p| match &p.kind {
            PatKind::Or(ps) => {
                ps[0].each_binding_or_first(f);
                false
            }
            PatKind::Binding(bm, _, ident, _) => {
                f(*bm, p.hir_id, p.span, *ident);
                true
            }
            _ => true,
        })
    }

    /// Checks if the pattern contains any patterns that bind something to
    /// an ident, e.g., `foo`, or `Foo(foo)` or `foo @ Bar(..)`.
    pub fn contains_bindings(&self) -> bool {
        self.satisfies(|p| matches!(p.kind, PatKind::Binding(..)))
    }

    /// Checks if the pattern satisfies the given predicate on some sub-pattern.
    fn satisfies(&self, pred: impl Fn(&hir::Pat<'_>) -> bool) -> bool {
        let mut satisfies = false;
        self.walk_short(|p| {
            if pred(p) {
                satisfies = true;
                false // Found one, can short circuit now.
            } else {
                true
            }
        });
        satisfies
    }

    pub fn simple_ident(&self) -> Option<Ident> {
        match self.kind {
            PatKind::Binding(
                hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
                _,
                ident,
                None,
            ) => Some(ident),
            _ => None,
        }
    }

    /// Returns variants that are necessary to exist for the pattern to match.
    pub fn necessary_variants(&self) -> Vec<DefId> {
        let mut variants = vec![];
        self.walk(|p| match &p.kind {
            PatKind::Or(_) => false,
            PatKind::Path(hir::QPath::Resolved(_, path))
            | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..)
            | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => {
                if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) =
                    path.res
                {
                    variants.push(id);
                }
                true
            }
            _ => true,
        });
        // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
        // the bounds
        let mut duplicates = FxHashSet::default();
        variants.retain(|def_id| duplicates.insert(*def_id));
        variants
    }

    /// Checks if the pattern contains any `ref` or `ref mut` bindings, and if
    /// yes whether it contains mutable or just immutables ones.
    //
    // FIXME(tschottdorf): this is problematic as the HIR is being scraped, but
    // ref bindings are be implicit after #42640 (default match binding modes). See issue #44848.
    pub fn contains_explicit_ref_binding(&self) -> Option<hir::Mutability> {
        let mut result = None;
        self.each_binding(|annotation, _, _, _| match annotation {
            hir::BindingAnnotation::Ref => match result {
                None | Some(hir::Mutability::Not) => result = Some(hir::Mutability::Not),
                _ => {}
            },
            hir::BindingAnnotation::RefMut => result = Some(hir::Mutability::Mut),
            _ => {}
        });
        result
    }
}
<|endoftext|>


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

/// Basic structure of a Monitor (aka Server App, aka wtttttt)
///
/// Monitors are designed so that the FoxBox can offer a simple
/// IFTTT-style Web UX to let users write their own scripts. More
/// complex monitors can installed from the web from a master device
/// (i.e. the user's cellphone or smart tv).

use dependencies::{DeviceKind, InputCapability, OutputCapability, Device, Range, Watcher, Witness};

use std::time::Duration;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;

extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

extern crate itertools;
use self::itertools::Zip;

/// A Monitor Application, i.e. an application (or a component of an
/// application) executed on the server.
///
/// Monitor applications are typically used for triggering an action
/// in reaction to an event: changing temperature when night falls,
/// ringing an alarm when a door is opened, etc.
///
/// Monitor applications are installed from a paired device. They may
/// either be part of a broader application (which can install them
/// through a web/REST API) or live on their own.
#[derive(Clone)]
struct MonitorApp {
    metadata: (), // FIXME: Authorizations, author, description, update url, version, ...

    /// `true` if the app is on, `false` otherwise.
    is_active: bool,

    /// Monitor applications have sets of requirements (e.g. "I need a
    /// camera"), which are allocated to actual resources through the
    /// UX. Re-allocating resources may be requested by the user, the
    /// foxbox, or an application, e.g. when replacing a device or
    /// upgrading the app.
    ///
    /// The position in the vector is important, as it is used to
    /// represent the instances of resources in the script.
    ///
    /// FIXME: Turn this `Vec` (and others) into a data structure in
    /// which this indexing property is built-in.
    requirements: Vec<Named<Requirement>>,

    /// For each requirement, the resources actually allocated to
    /// match the requirements. This may be 1 or more individual
    /// devices.
    ///
    /// Allocations can be done in several ways:
    ///
    /// - when entering the script through a UX (either the script or
    ///   the UX can suggest allocations);
    /// - when installing an application with a web front-end.
    allocations: Vec<Vec<Named<Arc<Device>>>>,

    code: Vec<Trigger>,

    is_running: bool,
}

impl<'a> MonitorApp {
/*
    Ideally, I'd like something along these lines.
    But, as rustc kindly pointed out to me, that's pretty much unsafe.

    fn iter_state_index(&'a self) -> Box<Iterator<Item=InputIndex> + 'a>
    {
        Box::new(
            Zip::new((&self.requirements, &self.allocations, 0..)).
                flat_map(|(req, allocation, allocation_index)| {
                    let allocation_index_clone = allocation_index.clone();
                    Zip::new((allocation, 0..)).
                        flat_map(|(individual_device, individual_device_index)| {
                            Zip::new((&req.data.inputs, 0..)).
                                map(|(_, individual_input_index)| {
                                    InputIndex {
                                        allocation: allocation_index_clone,
                                        device: individual_device_index,
                                        input: individual_input_index
                                    }
                                })
                        })
                }))
}
     */
    fn iter_state_index(&'a self) -> Vec<InputIndex>
    {
        let mut vec = Vec::new();
        for (req, allocation, allocation_index) in Zip::new((&self.requirements, &self.allocations, 0..)) {
            for (individual_device, individual_device_index) in Zip::new((allocation, 0..)) {
                for (input, individual_input_index) in Zip::new((&req.data.inputs, 0..)) {
                    vec.push(InputIndex {
                        allocation: allocation_index,
                        device: individual_device_index,
                        input: individual_input_index
                    })
                }
            }
        }
        vec
    }

    fn get_individual_device(&self, index: &InputIndex) -> Arc<Device> {
        self.allocations[index.allocation][index.device].data.clone()
    }

    fn get_input(&self, index: &InputIndex) -> InputCapability {
        self.requirements[index.allocation].data.inputs[index.input].data.clone()
    }
}

struct InputIndex {
    allocation: usize,
    device: usize,
    input: usize,
}

struct ConditionState {
    are_conditions_met: bool
}

type InputState = Vec<Vec<Vec<Option<Json>>>>;
struct MonitorTaskState {
    // Invariant: len() is the same as that of app.code
    trigger_condition_state: Vec<ConditionState>,

    /// For each device set allocated, for each individual device
    /// allocated, for each input watched by the app, the latest state
    /// received.
    // Invariant: outer len() is the same as that of self.requirements.
    // Invariant: inner len() is the same as number of input devices bound
    // to the corresponding requirement. May be empty.
    input_state: InputState,

    witnesses: Vec<Witness>,

    app: MonitorApp,
}
impl MonitorTaskState {
    fn set_state(&mut self, index: &InputIndex, value: Option<Json>) {
        self.input_state[index.allocation][index.device][index.input] = value;
    }
}

struct MonitorComm {
    tx: Sender<MonitorOp>,
    rx: Receiver<MonitorOp>,
}
struct MonitorTask {
    state: MonitorTaskState,
    comm: MonitorComm,
}

impl MonitorTask {
    fn new(app: MonitorApp) -> Self {
        // Initialize condition state.
        let mut trigger_condition_state = Vec::with_capacity(app.code.len());
        for _ in &app.code {
            trigger_condition_state.push(ConditionState {
                are_conditions_met: false,
            });
        }
        assert_eq!(trigger_condition_state.len(), app.code.len());

        // Initialize input state.
        let mut full_input_state = Vec::with_capacity(app.requirements.len());
        for (req, allocation) in app.requirements.iter().zip(&app.allocations) {

            // State for this allocation. A single allocation may map
            // to a number of inputs (e.g. "all fire alarms").
            let mut allocation_state = Vec::with_capacity(allocation.len());
            for individual_device in allocation {
                let mut individual_device_state = Vec::with_capacity(req.data.inputs.len());
                for _ in 0 .. req.data.inputs.len() {
                    individual_device_state.push(None);
                }
                allocation_state.push(individual_device_state);
            }
            assert_eq!(allocation_state.len(), allocation.len());
            full_input_state.push(allocation_state);
        }
        assert_eq!(full_input_state.len(), app.requirements.len());

        // Start watching
        let mut watcher = Watcher::new();
        let mut witnesses = Vec::new();
        let (tx, rx) = channel();

        for state_index in app.iter_state_index() {
            // FIXME: We currently use `Range::any()` for simplicity.
            // However, in most cases, we should be able to look inside
            // the condition to build a better `Range`.
            witnesses.push(
                watcher.add(
                    &app.get_individual_device(&state_index), // FIXME: Implement
                    &app.get_input(&state_index), // FIXME: Implement
                    &Range::any(),
                    |data| {
                        let _ = tx.send(MonitorOp::Update {
                            data: data,
                            index: state_index,
                        });
                        // Ignore errors. If the thread is shutting down, it's ok to lose messages.
                    }));
        }

        MonitorTask {
            state: MonitorTaskState {
                trigger_condition_state: trigger_condition_state,
                input_state: full_input_state,
                witnesses: witnesses,
                app: app,
            },
            comm: MonitorComm {
                tx: tx,
                rx: rx,
            }
        }
    }

    fn run(&mut self) {
        for msg in &self.comm.rx {
            use self::MonitorOp::*;
            match msg {
                Update {
                    data,
                    index,
                } => {
                    // Update the state
                    self.state.set_state(&index, Some(data));

                    // Find out if we should execute triggers.
                    // FIXME: We could optimize this by finding out which triggers
                    // are tainted by the update and only rechecking these.
                    for (trigger, trigger_condition_state) in Zip::new((&self.state.app.code, &mut self.state.trigger_condition_state)) {
                        let is_met = trigger.condition.is_met(&self.state.input_state);
                        if is_met == trigger_condition_state.are_conditions_met {
                            // No change in conditions. Nothing to do.
                            continue;
                        }
                        trigger_condition_state.are_conditions_met = is_met;
                        if !is_met {
                            // Conditions were met, now they are not anymore.
                            // The next time they are met, we can trigger
                            // a new execution.
                            continue;
                        }
                        // Conditions were not met, now they are, so it is
                        // time to start executing. We copy the inputs
                        // and dispatch to a background thread

                        // FIXME: Handle cooldown.

                        trigger_condition_state.are_conditions_met = true;
                        let _ = self.comm.tx.send(MonitorOp::Execute {
                            state: self.state.input_state.clone(),
                            commands: trigger.execute.clone()
                        });
                        // Ignore errors. If the thread is shutting down, it's ok to lose messages.
                    }
                },
                Execute {..} => {
                    panic!("Not implemented");
                }
                Stop => {
                    // Clean up watcher, stop the thread.
                    self.state.witnesses.clear();
                    return;
                }
            }
        }
    }
}

enum MonitorOp {
    Update{data: Json, index: InputIndex},
    Execute{state: InputState, commands: Vec<Command>},
    Stop
}

impl MonitorApp {
    pub fn start(&mut self) {
        if self.is_running {
            return;
        }
        self.is_running = true;

        let mut task = MonitorTask::new(self.clone());
        thread::spawn(move || {
            task.run();
        });
    }
}

/// Data labelled with a user-readable name.
#[derive(Clone)]
struct Named<T> {
    /// User-readable name.
    name: String,

    data: T,
}

/// A resource needed by this application. Typically, a definition of
/// device with some input our output capabilities.
#[derive(Clone)]
struct Requirement {
    /// The kind of resource, e.g. "a flashbulb".
    kind: DeviceKind,

    /// Input capabilities we need from the device, e.g. "the time of
    /// day", "the current temperature".
    inputs: Vec<Named<InputCapability>>,

    /// Output capabilities we need from the device, e.g. "play a
    /// sound", "set luminosity".
    outputs: Vec<Named<OutputCapability>>,

    /// Minimal number of resources required. If unspecified in the
    /// script, this is 1.
    min: u32,

    /// Maximal number of resources that may be handled. If
    /// unspecified in the script, this is the same as `min`.
    max: u32,

    // FIXME: We may need cooldown properties.
}

/// A single trigger, i.e. "when some condition becomes true, do
/// something".
#[derive(Clone)]
struct Trigger {
    /// The condition in which to execute the trigger.
    condition: Disjunction,

    /// Stuff to do once `condition` is met.
    execute: Vec<Command>,

    /// Minimal duration between two executions of the trigger.  If a
    /// duration was not picked by the developer, a reasonable default
    /// duration should be picked (e.g. 10 minutes).
    cooldown: Duration,
}

/// A disjunction (e.g. a "or") of conditions.
///
/// # Example
///
/// Door alarm #1 OR door alarm #2
#[derive(Clone)]
struct Disjunction {
    /// The disjunction is true iff any of the following conjunctions is true.
    any: Vec<Conjunction>
}

impl Disjunction {
    fn is_met(&self, input_state: &InputState) -> bool {
        panic!("Not implemented");
    }
}

/// A conjunction (e.g. a "and") of conditions.
#[derive(Clone)]
struct Conjunction {
    /// The conjunction is true iff all of the following expressions evaluate to true.
    all: Vec<Expression>
}

#[derive(Clone)]
enum Value {
    Json(Json),
    Blob{data: Arc<Vec<u8>>, mime_type: String},
}

/// An expression in the language.  Note that expressions may contain
/// inputs, which are typically asynchronous. Consequently,
/// expressions are evaluated asynchronously.
#[derive(Clone)]
enum Expression {
    Const {
        value: Value,

        /// We are dealing with real-world values, so physical units
        /// will prevent real-world accidents.
        unit: (), // FIXME: An actual unit

        /// The source for this value.  Used whenever we need to find
        /// out which sensor triggered the trigger (e.g. "where is the
        /// fire?").
        sources: Vec<usize>,
    },

    /// Dynamic values, including both actual sensors and higher-level values.
    ///
    /// # Example
    ///
    /// "Is there movement on motion detector" (may be true/false)
    ///
    /// # Example
    ///
    /// "Date of the latest motion on motion detector" (a Date)
    Input {
        /// A reference to the device used for input.
        /// This is an index in `requirements` and `allocations`.
        index: usize, // FIXME: We should use a custom type.

        /// A property to fetch (e.g. "luminosity" or "meta/latest-on").
        property: InputCapability,
    },

    /// Pure functions on values. The fact that they are pure
    /// functions is important, as it lets us find out automatically
    /// which subexpressions (including inputs) do not need to be
    /// re-evaluated.
    Function {
        function: Function,
        // FIXME: use a Box<> for now to avoid recursive type.
        arguments: Vec<Box<Expression>>
    },
}

#[derive(Clone)]
enum Function {
    // Operations on all values.
    InRange,
    OutOfRange,
    // Operations on strings
    Contains,
    NotContains,
}

/*
enum Function {
    // Operations on all values.
    Equals,
    NotEquals,

    // Operations on numbers, dates, durations.
    GreaterEq,
    Greater,
    LowerEq,
    Lower,
    Plus,
    Minus,

    // Operations on strings
    Contains,
    NotContains,

    // Etc.  FIXME: We'll need operations on dates, extracting name
    // from device, etc.
}
 */

/// Stuff to actually do. In practice, this is always a REST call.
// FIXME: Need to decide how much we wish to sandbox apps.
#[derive(Clone)]
struct Command {
    /// The resource to which this command applies,
    /// as an index in Trigger.requirements/allocations.
    destination: usize,  // FIXME: Use custom type.

    action: OutputCapability,

    arguments: HashMap<String, Option<Expression>>
}

<commit_msg>[fxbox/thinkerbell] Doc and cleanup<commit_after>
#![allow(unused_variables)]
#![allow(dead_code)]

/// Basic structure of a Monitor (aka Server App, aka wtttttt)
///
/// Monitors are designed so that the FoxBox can offer a simple
/// IFTTT-style Web UX to let users write their own scripts. More
/// complex monitors can installed from the web from a master device
/// (i.e. the user's cellphone or smart tv).

use dependencies::{DeviceKind, InputCapability, OutputCapability, Device, Range, Watcher, Witness};

use std::time::Duration;
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;

extern crate rustc_serialize;
use self::rustc_serialize::json::Json;

extern crate itertools;
use self::itertools::Zip;

/// A Monitor Application, i.e. an application (or a component of an
/// application) executed on the server.
///
/// Monitor applications are typically used for triggering an action
/// in reaction to an event: changing temperature when night falls,
/// ringing an alarm when a door is opened, etc.
///
/// Monitor applications are installed from a paired device. They may
/// either be part of a broader application (which can install them
/// through a web/REST API) or live on their own.
#[derive(Clone)]
struct MonitorApp {
    metadata: (), // FIXME: Authorizations, author, description, update url, version, ...

    /// `true` if the app is on, `false` otherwise.
    is_active: bool,

    /// Monitor applications have sets of requirements (e.g. "I need a
    /// camera"), which are allocated to actual resources through the
    /// UX. Re-allocating resources may be requested by the user, the
    /// foxbox, or an application, e.g. when replacing a device or
    /// upgrading the app.
    ///
    /// The position in the vector is important, as it is used to
    /// represent the instances of resources in the script.
    ///
    /// FIXME: Turn this `Vec` (and others) into a data structure in
    /// which this indexing property is built-in.
    requirements: Vec<Named<Requirement>>,

    /// For each requirement, the resources actually allocated to
    /// match the requirements. This may be 1 or more individual
    /// devices.
    ///
    /// Allocations can be done in several ways:
    ///
    /// - when entering the script through a UX (either the script or
    ///   the UX can suggest allocations);
    /// - when installing an application with a web front-end.
    allocations: Vec<Vec<Named<Arc<Device>>>>,

    code: Vec<Trigger>,

    is_running: bool,
}

impl<'a> MonitorApp {
    /// Index-safe iteration through all the possible
    /// allocations/individual devices/inputs.
    fn iter_state_index(&self) -> Vec<InputBinding>
    {
        // FIXME: Several possible optimizations here, including
        // caching the vector.
        let mut vec = Vec::new();
        for (req, allocation, allocation_index) in Zip::new((&self.requirements, &self.allocations, 0..)) {
            for (individual_device, individual_device_index) in Zip::new((allocation, 0..)) {
                for (input, individual_input_index) in Zip::new((&req.data.inputs, 0..)) {
                    vec.push(InputBinding {
                        allocation: allocation_index,
                        device: individual_device_index,
                        input: individual_input_index
                    })
                }
            }
        }
        vec
    }
}

/// The binding of an input capability to a specific device set.
struct InputBinding {
    /// The device set holding this input capability.
    /// Index in `app.allocations`.
    allocation: usize,

    /// The individual device holding this input capability.
    /// Index in `app.allocations[self.allocation]`.
    device: usize,

    /// The specific input holding this input capability.
    /// Index in `app.allocations[self.allocation][self.device]`.
    input: usize,
}

impl InputBinding {
    /// Get the device providing the InputBinding.
    fn get_individual_device(&self, app: &MonitorApp) -> Arc<Device> {
        app.allocations[self.allocation][self.device].data.clone()
    }

    /// Get the input capability for this binding.
    fn get_input(&self, app: &MonitorApp) -> InputCapability {
        app.requirements[self.allocation].data.inputs[self.input].data.clone()
    }

    /// Update the state attached to this input binding.
    fn set_state(&self, state: &mut MonitorTaskState, value: Option<Json>) {
        state.input_state[self.allocation][self.device][self.input] = value;
    }
}

/// The state of a given condition.
struct ConditionState {
    /// `true` if the conditions were met last time the state of the
    /// inputs changed. We use this to trigger an action only when
    /// conditions were previously unmet and are now met.
    are_conditions_met: bool,
    // FIXME: In the future, the cooldown should go here.
}

type InputState = Vec<Vec<Vec<Option<Json>>>>;
struct MonitorTaskState {
    // Invariant: len() is the same as that of app.code
    trigger_condition_state: Vec<ConditionState>,

    /// For each device set allocated, for each individual device
    /// allocated, for each input watched by the app, the latest state
    /// received.
    // Invariant: outer len() is the same as that of self.requirements.
    // Invariant: inner len() is the same as number of input devices bound
    // to the corresponding requirement. May be empty.
    input_state: InputState,

    witnesses: Vec<Witness>,

    app: MonitorApp,
}

struct MonitorComm {
    tx: Sender<MonitorOp>,
    rx: Receiver<MonitorOp>,
}
struct MonitorTask {
    state: MonitorTaskState,
    comm: MonitorComm,
}

impl MonitorTask {
    fn new(app: MonitorApp) -> Self {
        // Initialize condition state.
        let mut trigger_condition_state = Vec::with_capacity(app.code.len());
        for _ in &app.code {
            trigger_condition_state.push(ConditionState {
                are_conditions_met: false,
            });
        }
        assert_eq!(trigger_condition_state.len(), app.code.len());

        // Initialize input state.
        let mut full_input_state = Vec::with_capacity(app.requirements.len());
        for (req, allocation) in app.requirements.iter().zip(&app.allocations) {

            // State for this allocation. A single allocation may map
            // to a number of inputs (e.g. "all fire alarms").
            let mut allocation_state = Vec::with_capacity(allocation.len());
            for individual_device in allocation {
                let mut individual_device_state = Vec::with_capacity(req.data.inputs.len());
                for _ in 0 .. req.data.inputs.len() {
                    individual_device_state.push(None);
                }
                allocation_state.push(individual_device_state);
            }
            assert_eq!(allocation_state.len(), allocation.len());
            full_input_state.push(allocation_state);
        }
        assert_eq!(full_input_state.len(), app.requirements.len());

        // Start watching
        let mut watcher = Watcher::new();
        let mut witnesses = Vec::new();
        let (tx, rx) = channel();

        for state_index in app.iter_state_index() {
            // FIXME: We currently use `Range::any()` for simplicity.
            // However, in most cases, we should be able to look inside
            // the condition to build a better `Range`.
            witnesses.push(
                watcher.add(
                    &state_index.get_individual_device(&app),
                    &state_index.get_input(&app),
                    &Range::any(),
                    |data| {
                        let _ = tx.send(MonitorOp::Update {
                            data: data,
                            index: state_index,
                        });
                        // Ignore errors. If the thread is shutting down, it's ok to lose messages.
                    }));
        }

        MonitorTask {
            state: MonitorTaskState {
                trigger_condition_state: trigger_condition_state,
                input_state: full_input_state,
                witnesses: witnesses,
                app: app,
            },
            comm: MonitorComm {
                tx: tx,
                rx: rx,
            }
        }
    }

    fn run(&mut self) {
        for msg in &self.comm.rx {
            use self::MonitorOp::*;
            match msg {
                Update {
                    data,
                    index,
                } => {
                    // Update the state
                    index.set_state(&mut self.state, Some(data));

                    // Find out if we should execute triggers.
                    // FIXME: We could optimize this by finding out which triggers
                    // are tainted by the update and only rechecking these.
                    for (trigger, trigger_condition_state) in Zip::new((&self.state.app.code, &mut self.state.trigger_condition_state)) {
                        let is_met = trigger.condition.is_met(&self.state.input_state);
                        if is_met == trigger_condition_state.are_conditions_met {
                            // No change in conditions. Nothing to do.
                            continue;
                        }
                        trigger_condition_state.are_conditions_met = is_met;
                        if !is_met {
                            // Conditions were met, now they are not anymore.
                            // The next time they are met, we can trigger
                            // a new execution.
                            continue;
                        }
                        // Conditions were not met, now they are, so it is
                        // time to start executing. We copy the inputs
                        // and dispatch to a background thread

                        // FIXME: Handle cooldown.

                        trigger_condition_state.are_conditions_met = true;
                        let _ = self.comm.tx.send(MonitorOp::Execute {
                            state: self.state.input_state.clone(),
                            commands: trigger.execute.clone()
                        });
                        // Ignore errors. If the thread is shutting down, it's ok to lose messages.
                    }
                },
                Execute {..} => {
                    panic!("Not implemented");
                }
                Stop => {
                    // Clean up watcher, stop the thread.
                    self.state.witnesses.clear();
                    return;
                }
            }
        }
    }
}

enum MonitorOp {
    Update{data: Json, index: InputBinding},
    Execute{state: InputState, commands: Vec<Command>},
    Stop
}

impl MonitorApp {
    pub fn start(&mut self) {
        if self.is_running {
            return;
        }
        self.is_running = true;

        let mut task = MonitorTask::new(self.clone());
        thread::spawn(move || {
            task.run();
        });
    }
}

/// Data labelled with a user-readable name.
#[derive(Clone)]
struct Named<T> {
    /// User-readable name.
    name: String,

    data: T,
}

/// A resource needed by this application. Typically, a definition of
/// device with some input our output capabilities.
#[derive(Clone)]
struct Requirement {
    /// The kind of resource, e.g. "a flashbulb".
    kind: DeviceKind,

    /// Input capabilities we need from the device, e.g. "the time of
    /// day", "the current temperature".
    inputs: Vec<Named<InputCapability>>,

    /// Output capabilities we need from the device, e.g. "play a
    /// sound", "set luminosity".
    outputs: Vec<Named<OutputCapability>>,

    /// Minimal number of resources required. If unspecified in the
    /// script, this is 1.
    min: u32,

    /// Maximal number of resources that may be handled. If
    /// unspecified in the script, this is the same as `min`.
    max: u32,

    // FIXME: We may need cooldown properties.
}

/// A single trigger, i.e. "when some condition becomes true, do
/// something".
#[derive(Clone)]
struct Trigger {
    /// The condition in which to execute the trigger.
    condition: Disjunction,

    /// Stuff to do once `condition` is met.
    execute: Vec<Command>,

    /// Minimal duration between two executions of the trigger.  If a
    /// duration was not picked by the developer, a reasonable default
    /// duration should be picked (e.g. 10 minutes).
    cooldown: Duration,
}

/// A disjunction (e.g. a "or") of conditions.
///
/// # Example
///
/// Door alarm #1 OR door alarm #2
#[derive(Clone)]
struct Disjunction {
    /// The disjunction is true iff any of the following conjunctions is true.
    any: Vec<Conjunction>
}

impl Disjunction {
    fn is_met(&self, input_state: &InputState) -> bool {
        panic!("Not implemented");
    }
}

/// A conjunction (e.g. a "and") of conditions.
#[derive(Clone)]
struct Conjunction {
    /// The conjunction is true iff all of the following expressions evaluate to true.
    all: Vec<Expression>
}

#[derive(Clone)]
enum Value {
    Json(Json),
    Blob{data: Arc<Vec<u8>>, mime_type: String},
}

/// An expression in the language.  Note that expressions may contain
/// inputs, which are typically asynchronous. Consequently,
/// expressions are evaluated asynchronously.
#[derive(Clone)]
enum Expression {
    Const {
        value: Value,

        /// We are dealing with real-world values, so physical units
        /// will prevent real-world accidents.
        unit: (), // FIXME: An actual unit

        /// The source for this value.  Used whenever we need to find
        /// out which sensor triggered the trigger (e.g. "where is the
        /// fire?").
        sources: Vec<usize>,
    },

    /// Dynamic values, including both actual sensors and higher-level values.
    ///
    /// # Example
    ///
    /// "Is there movement on motion detector" (may be true/false)
    ///
    /// # Example
    ///
    /// "Date of the latest motion on motion detector" (a Date)
    Input {
        /// A reference to the device used for input.
        /// This is an index in `requirements` and `allocations`.
        index: usize, // FIXME: We should use a custom type.

        /// A property to fetch (e.g. "luminosity" or "meta/latest-on").
        property: InputCapability,
    },

    /// Pure functions on values. The fact that they are pure
    /// functions is important, as it lets us find out automatically
    /// which subexpressions (including inputs) do not need to be
    /// re-evaluated.
    Function {
        function: Function,
        // FIXME: use a Box<> for now to avoid recursive type.
        arguments: Vec<Box<Expression>>
    },
}

#[derive(Clone)]
enum Function {
    // Operations on all values.
    InRange,
    OutOfRange,
    // Operations on strings
    Contains,
    NotContains,
}

/*
enum Function {
    // Operations on all values.
    Equals,
    NotEquals,

    // Operations on numbers, dates, durations.
    GreaterEq,
    Greater,
    LowerEq,
    Lower,
    Plus,
    Minus,

    // Operations on strings
    Contains,
    NotContains,

    // Etc.  FIXME: We'll need operations on dates, extracting name
    // from device, etc.
}
 */

/// Stuff to actually do. In practice, this is always a REST call.
// FIXME: Need to decide how much we wish to sandbox apps.
#[derive(Clone)]
struct Command {
    /// The resource to which this command applies,
    /// as an index in Trigger.requirements/allocations.
    destination: usize,  // FIXME: Use custom type.

    action: OutputCapability,

    arguments: HashMap<String, Option<Expression>>
}

<|endoftext|>


<commit_before>extern crate cargo;
extern crate reqwest;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::fs::File;
use std::io::{Read, Write};

use cargo::core::{Dependency, Registry, Source, SourceId, Summary};
use cargo::core::resolver::{self, Method, Resolve};
use cargo::sources::RegistrySource;
use cargo::util::Config;

#[derive(Debug, Deserialize)]
struct TopCrates {
    crates: Vec<Crate>,
}

#[derive(Debug, Deserialize)]
struct Crate {
    #[serde(rename="id")]
    name: String,
    #[serde(rename="max_version")]
    version: String,
}

/// A Cargo.toml file.
#[derive(Serialize)]
struct TomlManifest {
    package: Package,
    dependencies: BTreeMap<String, String>,
}

/// Header of Cargo.toml file.
#[derive(Serialize)]
struct Package {
    name: String,
    version: String,
    authors: Vec<String>,
}

/// A mapping of a crates name to its identifier used in source code
#[derive(Debug, Serialize)]
struct CrateInformation {
    name: String,
    version: String,
    id: String,
}

/// Hand-curated changes to the crate list
#[derive(Debug, Deserialize)]
struct Modifications {
    blacklist: Vec<String>,
}

impl Modifications {
    fn blacklisted(&self, name: &str) -> bool {
        self.blacklist.iter().any(|n| n == name)
    }
}

lazy_static! {
    static ref MODIFICATIONS: Modifications = {
        let mut f = File::open("crate-modifications.toml")
            .expect("unable to open crate modifications file");

        let mut d = Vec::new();
        f.read_to_end(&mut d)
            .expect("unable to read crate modifications file");

        toml::from_slice(&d)
            .expect("unable to parse crate modifications file")
    };
}

fn get_top_crates() -> TopCrates {
    let resp =
        reqwest::get("https://crates.io/api/v1/crates?page=1&per_page=100&sort=downloads")
        .expect("Could not fetch top crates");
    assert!(resp.status().is_success());

    serde_json::from_reader(resp).expect("Invalid JSON")
}

fn decide_features(summary: &Summary) -> Method<'static> {
    lazy_static! {
        static ref PLAYGROUND_FEATURES: Vec<String> = vec!["playground".to_owned()];
    }

    // Enable `playground` feature if present.
    if summary.features().contains_key("playground") {
        Method::Required {
            dev_deps: false,
            features: &*PLAYGROUND_FEATURES,
            uses_default_features: false,
        }
    } else {
        Method::Required {
            dev_deps: false,
            features: &[],
            uses_default_features: true,
        }
    }
}

fn unique_latest_crates(resolve: Resolve) -> BTreeMap<String, String> {
    let mut uniqs = BTreeMap::new();
    for pkg in resolve.iter() {
        if MODIFICATIONS.blacklisted(pkg.name()) {
            continue;
        }

        match uniqs.entry(pkg.name()) {
            Entry::Vacant(entry) => {
                // First time seeing this package.
                entry.insert(pkg.version());
            }
            Entry::Occupied(mut entry) => {
                // Seen before, keep the newest version.
                if &pkg.version() > entry.get() {
                    entry.insert(pkg.version());
                }
            }
        }
    }

    uniqs.into_iter()
         .map(|(name, version)| (name.to_string(), version.to_string()))
         .collect()
}

fn write_manifest(manifest: TomlManifest, path: &str) {
    let mut f = File::create(path).expect("Unable to create Cargo.toml");
    let content = toml::to_vec(&manifest).expect("Couldn't serialize TOML");
    f.write_all(&content).expect("Couldn't write Cargo.toml");
}

fn main() {
    // Setup to interact with cargo.
    let config = Config::default().expect("Unable to create default Cargo config");
    let crates_io = SourceId::crates_io(&config).expect("Unable to create crates.io source ID");
    let mut registry = RegistrySource::remote(&crates_io, &config);
    registry.update().expect("Unable to update registry");

    // List top 100 crates by number of downloads on crates.io.
    let top = get_top_crates();

    let mut summaries = Vec::new();
    for Crate { ref name, ref version } in top.crates {
        if MODIFICATIONS.blacklisted(name) {
            continue;
        }

        // Query the registry for summary of this crate.
        let dep = Dependency::parse_no_deprecated(name, Some(version), &crates_io)
            .unwrap_or_else(|e| panic!("Unable to parse dependency for {}:{}: {}", name, version, e));

        let matches = registry.query_vec(&dep).unwrap_or_else(|e| {
            panic!("Unable to query registry for {}:{}: {}", name, version, e);
        });
        if matches.len() != 1 {
            panic!("expected one registry match for `{}:{}`", name, version);
        }
        let summary = matches.into_iter().next().unwrap();

        // Add a dependency on this crate.
        let method = decide_features(&summary);
        summaries.push((summary, method));
    }

    // Resolve transitive dependencies.
    let res = resolver::resolve(&summaries, &[], &mut registry)
        .expect("Unable to resolve dependencies");

    // Construct playground's Cargo.toml.
    let unique_latest_crates = unique_latest_crates(res);
    let manifest = TomlManifest {
        package: Package {
            name: "playground".to_owned(),
            version: "0.0.1".to_owned(),
            authors: vec!["The Rust Playground".to_owned()],
        },
        dependencies: unique_latest_crates.clone(),
    };

    // Write manifest file.
    let cargo_toml = "result.Cargo.toml";
    write_manifest(manifest, cargo_toml);
    println!("wrote {}", cargo_toml);

    let mut infos = Vec::new();

    for (name, version) in unique_latest_crates {
        let pkgid = cargo::core::PackageId::new(&name, &version, &crates_io)
            .unwrap_or_else(|e| panic!("Unable to build PackageId for {} {}: {}", name, version, e));

        let pkg = registry.download(&pkgid)
            .unwrap_or_else(|e| panic!("Unable to download {} {}: {}", name, version, e));

        for target in pkg.targets() {
            if let cargo::core::TargetKind::Lib(_) = *target.kind() {
                infos.push(CrateInformation {
                    name: name.clone(),
                    version: version.clone(),
                    id: target.crate_name()
                })
            }
        }
    }

    let path = "crate-information.json";
    let mut f = File::create(path)
        .unwrap_or_else(|e| panic!("Unable to create {}: {}", path, e));
    serde_json::to_writer_pretty(&mut f, &infos)
        .unwrap_or_else(|e| panic!("Unable to write {}: {}", path, e));
    println!("Wrote {}", path);
}
<commit_msg>Allow adding curated crates to the available set<commit_after>extern crate cargo;
extern crate reqwest;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::collections::{BTreeMap, BTreeSet};
use std::collections::btree_map::Entry;
use std::fs::File;
use std::io::{Read, Write};

use cargo::core::{Dependency, Registry, Source, SourceId, Summary};
use cargo::core::resolver::{self, Method, Resolve};
use cargo::sources::RegistrySource;
use cargo::util::Config;

/// The list of crates from crates.io
#[derive(Debug, Deserialize)]
struct TopCrates {
    crates: Vec<Crate>,
}

/// A single crate from crates.io
#[derive(Debug, Deserialize)]
struct OneCrate {
    #[serde(rename="crate")]
    krate: Crate,
}

/// The shared description of a crate
#[derive(Debug, Deserialize)]
struct Crate {
    #[serde(rename="id")]
    name: String,
    #[serde(rename="max_version")]
    version: String,
}

/// A Cargo.toml file.
#[derive(Serialize)]
struct TomlManifest {
    package: Package,
    dependencies: BTreeMap<String, String>,
}

/// Header of Cargo.toml file.
#[derive(Serialize)]
struct Package {
    name: String,
    version: String,
    authors: Vec<String>,
}

/// A mapping of a crates name to its identifier used in source code
#[derive(Debug, Serialize)]
struct CrateInformation {
    name: String,
    version: String,
    id: String,
}

/// Hand-curated changes to the crate list
#[derive(Debug, Deserialize)]
struct Modifications {
    #[serde(default)]
    blacklist: Vec<String>,
    #[serde(default)]
    additions: BTreeSet<String>,
}

impl Modifications {
    fn blacklisted(&self, name: &str) -> bool {
        self.blacklist.iter().any(|n| n == name)
    }

    fn additions(&self, existing_names: &[&str]) -> Vec<&str> {
        let existing_names: BTreeSet<_> = existing_names.iter().collect();
        self.additions.iter()
            .map(|n| n.as_str())
            .filter(|n| !existing_names.contains(n))
            .collect()
    }
}

lazy_static! {
    static ref MODIFICATIONS: Modifications = {
        let mut f = File::open("crate-modifications.toml")
            .expect("unable to open crate modifications file");

        let mut d = Vec::new();
        f.read_to_end(&mut d)
            .expect("unable to read crate modifications file");

        toml::from_slice(&d)
            .expect("unable to parse crate modifications file")
    };
}

impl TopCrates {
    /// List top 100 crates by number of downloads on crates.io.
    fn download() -> TopCrates {
        let resp =
            reqwest::get("https://crates.io/api/v1/crates?page=1&per_page=100&sort=downloads")
            .expect("Could not fetch top crates");
        assert!(resp.status().is_success());

        serde_json::from_reader(resp).expect("Invalid JSON")
    }

    /// Add crates that have been hand-picked
    fn add_curated_crates(&mut self) {
        let added_crates: Vec<_> = {
            let names = self.names();
            let new_names = MODIFICATIONS.additions(&names);

            new_names.into_iter().map(|name| {
                let api_url = format!("https://crates.io/api/v1/crates/{}", name);

                let resp =
                    reqwest::get(&api_url)
                    .unwrap_or_else(|e| panic!("Could not fetch crate {}: {}", name, e));
                assert!(resp.status().is_success());

                let one: OneCrate = serde_json::from_reader(resp)
                    .unwrap_or_else(|e| panic!("Crate {} had invalid JSON: {}", name, e));

                one.krate
            }).collect()
        };

        self.crates.extend(added_crates);
    }

    fn names(&self) -> Vec<&str> {
        self.crates.iter().map(|c| c.name.as_str()).collect()
    }
}

fn decide_features(summary: &Summary) -> Method<'static> {
    lazy_static! {
        static ref PLAYGROUND_FEATURES: Vec<String> = vec!["playground".to_owned()];
    }

    // Enable `playground` feature if present.
    if summary.features().contains_key("playground") {
        Method::Required {
            dev_deps: false,
            features: &*PLAYGROUND_FEATURES,
            uses_default_features: false,
        }
    } else {
        Method::Required {
            dev_deps: false,
            features: &[],
            uses_default_features: true,
        }
    }
}

fn unique_latest_crates(resolve: Resolve) -> BTreeMap<String, String> {
    let mut uniqs = BTreeMap::new();
    for pkg in resolve.iter() {
        if MODIFICATIONS.blacklisted(pkg.name()) {
            continue;
        }

        match uniqs.entry(pkg.name()) {
            Entry::Vacant(entry) => {
                // First time seeing this package.
                entry.insert(pkg.version());
            }
            Entry::Occupied(mut entry) => {
                // Seen before, keep the newest version.
                if &pkg.version() > entry.get() {
                    entry.insert(pkg.version());
                }
            }
        }
    }

    uniqs.into_iter()
         .map(|(name, version)| (name.to_string(), version.to_string()))
         .collect()
}

fn write_manifest(manifest: TomlManifest, path: &str) {
    let mut f = File::create(path).expect("Unable to create Cargo.toml");
    let content = toml::to_vec(&manifest).expect("Couldn't serialize TOML");
    f.write_all(&content).expect("Couldn't write Cargo.toml");
}

fn main() {
    // Setup to interact with cargo.
    let config = Config::default().expect("Unable to create default Cargo config");
    let crates_io = SourceId::crates_io(&config).expect("Unable to create crates.io source ID");
    let mut registry = RegistrySource::remote(&crates_io, &config);
    registry.update().expect("Unable to update registry");

    let mut top = TopCrates::download();
    top.add_curated_crates();

    let mut summaries = Vec::new();
    for Crate { ref name, ref version } in top.crates {
        if MODIFICATIONS.blacklisted(name) {
            continue;
        }

        // Query the registry for summary of this crate.
        let dep = Dependency::parse_no_deprecated(name, Some(version), &crates_io)
            .unwrap_or_else(|e| panic!("Unable to parse dependency for {}:{}: {}", name, version, e));

        let matches = registry.query_vec(&dep).unwrap_or_else(|e| {
            panic!("Unable to query registry for {}:{}: {}", name, version, e);
        });
        if matches.len() != 1 {
            panic!("expected one registry match for `{}:{}`", name, version);
        }
        let summary = matches.into_iter().next().unwrap();

        // Add a dependency on this crate.
        let method = decide_features(&summary);
        summaries.push((summary, method));
    }

    // Resolve transitive dependencies.
    let res = resolver::resolve(&summaries, &[], &mut registry)
        .expect("Unable to resolve dependencies");

    // Construct playground's Cargo.toml.
    let unique_latest_crates = unique_latest_crates(res);
    let manifest = TomlManifest {
        package: Package {
            name: "playground".to_owned(),
            version: "0.0.1".to_owned(),
            authors: vec!["The Rust Playground".to_owned()],
        },
        dependencies: unique_latest_crates.clone(),
    };

    // Write manifest file.
    let cargo_toml = "result.Cargo.toml";
    write_manifest(manifest, cargo_toml);
    println!("wrote {}", cargo_toml);

    let mut infos = Vec::new();

    for (name, version) in unique_latest_crates {
        let pkgid = cargo::core::PackageId::new(&name, &version, &crates_io)
            .unwrap_or_else(|e| panic!("Unable to build PackageId for {} {}: {}", name, version, e));

        let pkg = registry.download(&pkgid)
            .unwrap_or_else(|e| panic!("Unable to download {} {}: {}", name, version, e));

        for target in pkg.targets() {
            if let cargo::core::TargetKind::Lib(_) = *target.kind() {
                infos.push(CrateInformation {
                    name: name.clone(),
                    version: version.clone(),
                    id: target.crate_name()
                })
            }
        }
    }

    let path = "crate-information.json";
    let mut f = File::create(path)
        .unwrap_or_else(|e| panic!("Unable to create {}: {}", path, e));
    serde_json::to_writer_pretty(&mut f, &infos)
        .unwrap_or_else(|e| panic!("Unable to write {}: {}", path, e));
    println!("Wrote {}", path);
}
<|endoftext|>


<commit_before>extern crate clap;

use std::cell::RefCell;
use self::clap::{App, ArgMatches};

pub fn cli<'a>() -> ArgMatches<'a> {
    App::new("Pathfinder")
        .version("0.1.0")
        .author("Valeryi Savich <relrin78@gmail.com>")
        .about("WebSocket-over-Kafka reverse proxy")
        .args_from_usage(
           "-c, --config=[FILE]       'Path to a custom settings file'
            -i, --ip=[IP]             'The used IP for a server'
            -p, --port=[PORT]         'The listened port'
            -C, --cert=[CERTIFICATE]  'Path to a used certificate'
            -K, --key=[KEY]           'Path to a public key'",
        )
        .get_matches()
}

pub fn get_value<'a>(cli: &'a ArgMatches<'a>, key: &'a str, default: &'a str) -> &'a str {
    let cli = RefCell::new(cli);
    let cli = cli.into_inner();
    cli.value_of(key).unwrap_or(default)
}
<commit_msg>Added tests for get_value function<commit_after>extern crate clap;

use std::cell::RefCell;
use self::clap::{App, ArgMatches};

pub fn cli<'a>() -> ArgMatches<'a> {
    App::new("Pathfinder")
        .version("0.1.0")
        .author("Valeryi Savich <relrin78@gmail.com>")
        .about("WebSocket-over-Kafka reverse proxy")
        .args_from_usage(
           "-c, --config=[FILE]       'Path to a custom settings file'
            -i, --ip=[IP]             'The used IP for a server'
            -p, --port=[PORT]         'The listened port'
            -C, --cert=[CERTIFICATE]  'Path to a used certificate'
            -K, --key=[KEY]           'Path to a public key'",
        )
        .get_matches()
}

pub fn get_value<'a>(cli: &'a ArgMatches<'a>, key: &'a str, default: &'a str) -> &'a str {
    let cli = RefCell::new(cli);
    let cli = cli.into_inner();
    cli.value_of(key).unwrap_or(default)
}

#[cfg(test)]
mod tests {
    use super::clap::{App, Arg};
    use super::{cli, get_value};

    #[test]
    fn test_get_value_returns_extracted_value() {
        let cli = App::new("Test CLI")
                    .version("0.1.0")
                    .arg(Arg::with_name("config")
                        .short("c")
                        .long("config")
                        .default_value("test")
                    )
                   .get_matches();
        assert_eq!(get_value(&cli, "config", "not found"), "test");
    }

    #[test]
    fn test_get_value_returns_default_value() {
        let cli = cli();
        assert_eq!(get_value(&cli, "WRONG_KEY", "not found"), "not found");
    }
}<|endoftext|>