<commit_before>use crate::format;
use crate::nodes::*;
use crate::parser;
use std::collections::HashSet;

pub fn translate(module: &Module) -> CompatModule {
    // Build the list of modules to link.
    // These are specified using the #link macros.
    let mut link: Vec<String> = Vec::new();
    for node in &module.elements {
        match node {
            ModuleObject::CompatMacro(x) => {
                if x.name == "link" {
                    link.push(x.value.clone())
                }
            }
            _ => {}
        }
    }

    // Translate each node into a C node. Some nodes are valid C nodes
    // already, but some are not and have to be converted to their
    // "Compat..." analogs.
    let mut elements: Vec<CompatModuleObject> = Vec::new();
    for element in &module.elements {
        for node in translate_node(element) {
            elements.push(node)
        }
    }

    // Since we all know what the standard C library is, just include it all
    // everywhere. We could analyze the module contents and add only those
    // headers that are required, but why bother.
    let std = vec![
        "assert", "ctype", "errno", "limits", "math", "stdarg", "stdbool", "stddef", "stdint",
        "stdio", "stdlib", "string", "time",
    ];
    for n in std {
        elements.push(CompatModuleObject::CompatInclude(CompatInclude {
            name: format!("<{}.h>", n),
        }));
    }

    // Reorder the elements so that typedefs and similar preamble elements
    // come first.
    let mut groups: Vec<Vec<CompatModuleObject>> = vec![
        vec![CompatModuleObject::CompatSplit {
            text: String::from("/* -------------- */"),
        }],
        vec![CompatModuleObject::CompatSplit {
            text: String::from("/* -------------- */"),
        }],
        vec![CompatModuleObject::CompatSplit {
            text: String::from("/* -------------- */"),
        }],
        vec![CompatModuleObject::CompatSplit {
            text: String::from("/* -------------- */"),
        }],
        vec![CompatModuleObject::CompatSplit {
            text: String::from("/* -------------- */"),
        }],
        vec![CompatModuleObject::CompatSplit {
            text: String::from("/* -------------- */"),
        }],
    ];
    let mut set: HashSet<String> = HashSet::new();
    for element in elements {
        let order = match element {
            CompatModuleObject::CompatInclude(_) => 0,
            CompatModuleObject::CompatMacro(_) => 0,
            CompatModuleObject::CompatStructForwardDeclaration(_) => 1,
            CompatModuleObject::Typedef(_) => 2,
            CompatModuleObject::CompatStructDefinition(_) => 3,
            CompatModuleObject::CompatEnum(_) => 3,
            CompatModuleObject::CompatFunctionForwardDeclaration(_) => 4,
            _ => 5,
        };

        match &element {
            CompatModuleObject::Typedef(x) => {
                let s = format::format_typedef(&x);
                if set.contains(&s) {
                    continue;
                }
                set.insert(s);
            }
            CompatModuleObject::CompatStructDefinition(x) => {
                let s = format::format_compat_struct_definition(&x);
                if set.contains(&s) {
                    continue;
                }
                set.insert(s);
            }
            _ => {}
        }
        groups[order].push(element);
    }
    let mut sorted_elements: Vec<CompatModuleObject> = vec![CompatModuleObject::CompatSplit {
        text: String::from(format!("/* -------{}------- */", &module.id)),
    }];
    for group in groups {
        for e in group {
            sorted_elements.push(e)
        }
    }

    return CompatModule {
        id: module.id.clone(),
        elements: sorted_elements,
        link,
    };
}

fn translate_node(node: &ModuleObject) -> Vec<CompatModuleObject> {
    match node {
        ModuleObject::Typedef(x) => translate_typedef(x),
        ModuleObject::Import(x) => {
            let module = parser::get_module(&x.path).unwrap();
            let compat = translate(&module);
            return get_module_synopsis(compat);
        }
        ModuleObject::FunctionDeclaration(x) => translate_function_declaration(x),
        ModuleObject::Enum(x) => {
            return vec![CompatModuleObject::CompatEnum(CompatEnum {
                members: x.members.clone(),
                is_hidden: !x.is_pub,
            })];
        }
        ModuleObject::CompatMacro(x) => {
            if x.name == "type" || x.name == "link" {
                return vec![];
            } else {
                return vec![CompatModuleObject::CompatMacro(x.clone())];
            }
        }
        ModuleObject::ModuleVariable(x) => vec![CompatModuleObject::ModuleVariable(x.clone())],
        // ModuleObject::CompatInclude(x) => vec![CompatModuleObject::CompatInclude(x.clone())],
    }
}

fn translate_typedef(node: &Typedef) -> Vec<CompatModuleObject> {
    match &node.type_name {
        // A sugary "typedef {int a} foo_t" is translated to
        // "struct __foo_t_struct {int a}; typedef __foo_t_struct foo_t;".
        // And remember that the struct definition itself is sugar that
        // should be translated as well.
        TypedefTarget::AnonymousStruct(s) => {
            let struct_name = format!("__{}_struct", node.form.alias);

            // Build the compat struct fields.
            let mut fields: Vec<CompatStructEntry> = Vec::new();
            for entry in &s.entries {
                match entry {
                    // One fieldlist is multiple fields of the same type.
                    StructEntry::StructFieldlist(x) => {
                        for f in &x.forms {
                            fields.push(CompatStructEntry::CompatStructField(CompatStructField {
                                type_name: x.type_name.clone(),
                                form: f.clone(),
                            }));
                        }
                    }
                    StructEntry::Union(x) => {
                        fields.push(CompatStructEntry::Union(x.clone()));
                    }
                }
            }
            vec![
                CompatModuleObject::CompatStructForwardDeclaration(
                    CompatStructForwardDeclaration {
                        kind: "c_compat_struct_forward_declaration".to_string(),
                        name: struct_name.clone(),
                    },
                ),
                CompatModuleObject::CompatStructDefinition(CompatStructDefinition {
                    kind: "c_compat_struct_definition".to_string(),
                    name: struct_name.clone(),
                    fields: fields,
                }),
                CompatModuleObject::Typedef(Typedef {
                    kind: "c_typedef".to_string(),
                    type_name: TypedefTarget::Type(Type {
                        kind: "c_type".to_string(),
                        is_const: false,
                        type_name: format!("struct {}", struct_name.clone()),
                    }),
                    form: TypedefForm {
                        stars: "".to_string(),
                        size: 0,
                        params: None,
                        alias: node.form.alias.clone(),
                    },
                }),
            ]
        }
        _ => vec![CompatModuleObject::Typedef(Typedef {
            kind: "c_typedef".to_string(),
            type_name: node.type_name.clone(),
            form: node.form.clone(),
        })],
    }
}

fn compat_function_forward_declaration(node: &CompatFunctionDeclaration) -> CompatModuleObject {
    return CompatModuleObject::CompatFunctionForwardDeclaration(
        CompatFunctionForwardDeclaration {
            is_static: node.is_static,
            type_name: node.type_name.clone(),
            form: node.form.clone(),
            parameters: node.parameters.clone(),
        },
    );
}

// Module synopsis is what you would usually extract into a header file:
// function prototypes, typedefs, struct declarations.
fn get_module_synopsis(module: CompatModule) -> Vec<CompatModuleObject> {
    let mut elements: Vec<CompatModuleObject> = vec![];

    for element in module.elements {
        match element {
            // Write out non-static function prototypes.
            // CompatModuleObject::CompatFunctionDeclaration(x) => {
            //     if !x.is_static {
            //         elements.push(compat_function_forward_declaration(&x));
            //     }
            // }
            CompatModuleObject::Typedef(x) => elements.push(CompatModuleObject::Typedef(x)),
            CompatModuleObject::CompatStructDefinition(x) => {
                elements.push(CompatModuleObject::CompatStructDefinition(x))
            }
            CompatModuleObject::CompatFunctionForwardDeclaration(x) => {
                if !x.is_static {
                    elements.push(CompatModuleObject::CompatFunctionForwardDeclaration(x))
                }
            }
            CompatModuleObject::CompatMacro(x) => elements.push(CompatModuleObject::CompatMacro(x)),
            CompatModuleObject::CompatEnum(x) => {
                if !x.is_hidden {
                    elements.push(CompatModuleObject::CompatEnum(x))
                }
            }
            _ => {}
        }
    }
    return elements;
}

fn translate_function_parameters(node: &FunctionParameters) -> CompatFunctionParameters {
    // One parameter, like in structs, is one type and multiple entries,
    // while the C parameters are always one type and one entry.
    let mut parameters: Vec<CompatFunctionParameter> = Vec::new();
    for parameter in &node.list {
        for form in &parameter.forms {
            parameters.push(CompatFunctionParameter {
                type_name: parameter.type_name.clone(),
                form: form.clone(),
            })
        }
    }
    return CompatFunctionParameters {
        list: parameters,
        variadic: node.variadic,
    };
}

fn translate_function_declaration(node: &FunctionDeclaration) -> Vec<CompatModuleObject> {
    let func = CompatFunctionDeclaration {
        kind: "c_compat_function_declaration".to_string(),
        is_static: !node.is_pub,
        type_name: node.type_name.clone(),
        form: node.form.clone(),
        parameters: translate_function_parameters(&node.parameters),
        body: node.body.clone(),
    };
    return vec![
        compat_function_forward_declaration(&func),
        CompatModuleObject::CompatFunctionDeclaration(func),
    ];
}
<commit_msg>Skip empty groups in output<commit_after>use crate::format;
use crate::nodes::*;
use crate::parser;
use std::collections::HashSet;

pub fn translate(module: &Module) -> CompatModule {
    // Build the list of modules to link.
    // These are specified using the #link macros.
    let mut link: Vec<String> = Vec::new();
    for node in &module.elements {
        match node {
            ModuleObject::CompatMacro(x) => {
                if x.name == "link" {
                    link.push(x.value.clone())
                }
            }
            _ => {}
        }
    }

    // Translate each node into a C node. Some nodes are valid C nodes
    // already, but some are not and have to be converted to their
    // "Compat..." analogs.
    let mut elements: Vec<CompatModuleObject> = Vec::new();
    for element in &module.elements {
        for node in translate_node(element) {
            elements.push(node)
        }
    }

    // Since we all know what the standard C library is, just include it all
    // everywhere. We could analyze the module contents and add only those
    // headers that are required, but why bother.
    let std = vec![
        "assert", "ctype", "errno", "limits", "math", "stdarg", "stdbool", "stddef", "stdint",
        "stdio", "stdlib", "string", "time",
    ];
    for n in std {
        elements.push(CompatModuleObject::CompatInclude(CompatInclude {
            name: format!("<{}.h>", n),
        }));
    }

    // Reorder the elements so that typedefs and similar preamble elements
    // come first.
    let mut groups: Vec<Vec<CompatModuleObject>> =
        vec![vec![], vec![], vec![], vec![], vec![], vec![]];
    let mut set: HashSet<String> = HashSet::new();
    for element in elements {
        let order = match element {
            CompatModuleObject::CompatInclude(_) => 0,
            CompatModuleObject::CompatMacro(_) => 0,
            CompatModuleObject::CompatStructForwardDeclaration(_) => 1,
            CompatModuleObject::Typedef(_) => 2,
            CompatModuleObject::CompatStructDefinition(_) => 3,
            CompatModuleObject::CompatEnum(_) => 3,
            CompatModuleObject::CompatFunctionForwardDeclaration(_) => 4,
            _ => 5,
        };

        match &element {
            CompatModuleObject::Typedef(x) => {
                let s = format::format_typedef(&x);
                if set.contains(&s) {
                    continue;
                }
                set.insert(s);
            }
            CompatModuleObject::CompatStructDefinition(x) => {
                let s = format::format_compat_struct_definition(&x);
                if set.contains(&s) {
                    continue;
                }
                set.insert(s);
            }
            _ => {}
        }
        groups[order].push(element);
    }
    let mut sorted_elements: Vec<CompatModuleObject> = vec![CompatModuleObject::CompatSplit {
        text: String::from(format!("/* -------{}------- */", &module.id)),
    }];
    for group in groups {
        if group.len() == 0 {
            continue;
        }
        sorted_elements.push(CompatModuleObject::CompatSplit {
            text: String::from("/* -------------- */"),
        });
        for e in group {
            sorted_elements.push(e)
        }
    }

    return CompatModule {
        id: module.id.clone(),
        elements: sorted_elements,
        link,
    };
}

fn translate_node(node: &ModuleObject) -> Vec<CompatModuleObject> {
    match node {
        ModuleObject::Typedef(x) => translate_typedef(x),
        ModuleObject::Import(x) => {
            let module = parser::get_module(&x.path).unwrap();
            let compat = translate(&module);
            return get_module_synopsis(compat);
        }
        ModuleObject::FunctionDeclaration(x) => translate_function_declaration(x),
        ModuleObject::Enum(x) => {
            return vec![CompatModuleObject::CompatEnum(CompatEnum {
                members: x.members.clone(),
                is_hidden: !x.is_pub,
            })];
        }
        ModuleObject::CompatMacro(x) => {
            if x.name == "type" || x.name == "link" {
                return vec![];
            } else {
                return vec![CompatModuleObject::CompatMacro(x.clone())];
            }
        }
        ModuleObject::ModuleVariable(x) => vec![CompatModuleObject::ModuleVariable(x.clone())],
        // ModuleObject::CompatInclude(x) => vec![CompatModuleObject::CompatInclude(x.clone())],
    }
}

fn translate_typedef(node: &Typedef) -> Vec<CompatModuleObject> {
    match &node.type_name {
        // A sugary "typedef {int a} foo_t" is translated to
        // "struct __foo_t_struct {int a}; typedef __foo_t_struct foo_t;".
        // And remember that the struct definition itself is sugar that
        // should be translated as well.
        TypedefTarget::AnonymousStruct(s) => {
            let struct_name = format!("__{}_struct", node.form.alias);

            // Build the compat struct fields.
            let mut fields: Vec<CompatStructEntry> = Vec::new();
            for entry in &s.entries {
                match entry {
                    // One fieldlist is multiple fields of the same type.
                    StructEntry::StructFieldlist(x) => {
                        for f in &x.forms {
                            fields.push(CompatStructEntry::CompatStructField(CompatStructField {
                                type_name: x.type_name.clone(),
                                form: f.clone(),
                            }));
                        }
                    }
                    StructEntry::Union(x) => {
                        fields.push(CompatStructEntry::Union(x.clone()));
                    }
                }
            }
            vec![
                CompatModuleObject::CompatStructForwardDeclaration(
                    CompatStructForwardDeclaration {
                        kind: "c_compat_struct_forward_declaration".to_string(),
                        name: struct_name.clone(),
                    },
                ),
                CompatModuleObject::CompatStructDefinition(CompatStructDefinition {
                    kind: "c_compat_struct_definition".to_string(),
                    name: struct_name.clone(),
                    fields: fields,
                }),
                CompatModuleObject::Typedef(Typedef {
                    kind: "c_typedef".to_string(),
                    type_name: TypedefTarget::Type(Type {
                        kind: "c_type".to_string(),
                        is_const: false,
                        type_name: format!("struct {}", struct_name.clone()),
                    }),
                    form: TypedefForm {
                        stars: "".to_string(),
                        size: 0,
                        params: None,
                        alias: node.form.alias.clone(),
                    },
                }),
            ]
        }
        _ => vec![CompatModuleObject::Typedef(Typedef {
            kind: "c_typedef".to_string(),
            type_name: node.type_name.clone(),
            form: node.form.clone(),
        })],
    }
}

fn compat_function_forward_declaration(node: &CompatFunctionDeclaration) -> CompatModuleObject {
    return CompatModuleObject::CompatFunctionForwardDeclaration(
        CompatFunctionForwardDeclaration {
            is_static: node.is_static,
            type_name: node.type_name.clone(),
            form: node.form.clone(),
            parameters: node.parameters.clone(),
        },
    );
}

// Module synopsis is what you would usually extract into a header file:
// function prototypes, typedefs, struct declarations.
fn get_module_synopsis(module: CompatModule) -> Vec<CompatModuleObject> {
    let mut elements: Vec<CompatModuleObject> = vec![];

    for element in module.elements {
        match element {
            // Write out non-static function prototypes.
            // CompatModuleObject::CompatFunctionDeclaration(x) => {
            //     if !x.is_static {
            //         elements.push(compat_function_forward_declaration(&x));
            //     }
            // }
            CompatModuleObject::Typedef(x) => elements.push(CompatModuleObject::Typedef(x)),
            CompatModuleObject::CompatStructDefinition(x) => {
                elements.push(CompatModuleObject::CompatStructDefinition(x))
            }
            CompatModuleObject::CompatFunctionForwardDeclaration(x) => {
                if !x.is_static {
                    elements.push(CompatModuleObject::CompatFunctionForwardDeclaration(x))
                }
            }
            CompatModuleObject::CompatMacro(x) => elements.push(CompatModuleObject::CompatMacro(x)),
            CompatModuleObject::CompatEnum(x) => {
                if !x.is_hidden {
                    elements.push(CompatModuleObject::CompatEnum(x))
                }
            }
            _ => {}
        }
    }
    return elements;
}

fn translate_function_parameters(node: &FunctionParameters) -> CompatFunctionParameters {
    // One parameter, like in structs, is one type and multiple entries,
    // while the C parameters are always one type and one entry.
    let mut parameters: Vec<CompatFunctionParameter> = Vec::new();
    for parameter in &node.list {
        for form in &parameter.forms {
            parameters.push(CompatFunctionParameter {
                type_name: parameter.type_name.clone(),
                form: form.clone(),
            })
        }
    }
    return CompatFunctionParameters {
        list: parameters,
        variadic: node.variadic,
    };
}

fn translate_function_declaration(node: &FunctionDeclaration) -> Vec<CompatModuleObject> {
    let func = CompatFunctionDeclaration {
        kind: "c_compat_function_declaration".to_string(),
        is_static: !node.is_pub,
        type_name: node.type_name.clone(),
        form: node.form.clone(),
        parameters: translate_function_parameters(&node.parameters),
        body: node.body.clone(),
    };
    return vec![
        compat_function_forward_declaration(&func),
        CompatModuleObject::CompatFunctionDeclaration(func),
    ];
}
<|endoftext|>


<commit_before>use std::env;
use std::path::PathBuf;
use std::fs;
use std::path::Path;
use std::collections::BTreeMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::fmt::Write as fmt_Write;

extern crate tempdir;
extern crate regex;

extern crate toml;

extern crate serde;
#[macro_use]
extern crate serde_derive;

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
struct TomlWorkspace {
    members: Vec<String>,
}

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
struct TomlPackage {
    name: String,
    version: String,
    publish: Option<bool>,
}

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
#[serde(rename_all = "kebab-case")]
pub struct DetailedTomlDependency {
    version: Option<String>,
    path: Option<String>
}

#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(untagged)]
pub enum TomlDependency {
    Simple(String),
    Detailed(DetailedTomlDependency),
}

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
struct TomlManifest {
    package: Option<TomlPackage>,
    workspace: Option<TomlWorkspace>,
    dependencies: Option<BTreeMap<String, TomlDependency>>,
}

fn find_repo_root() -> PathBuf {
    let candidates = &[".", ".."];
    for dir in candidates {
        let dir = Path::new(dir).to_owned();
        let mut file = dir.clone();
        file.push("CHANGELOG.md");
        if file.exists() {
            return dir;
        }
    }
    panic!("cannot find repository root");
}

/// Read `Cargo.toml` from specified directory.
fn read_crate_manifest(crate_path: &Path) -> TomlManifest {
    assert!(crate_path.is_dir());

    let mut cargo_toml = crate_path.to_owned();
    cargo_toml.push("Cargo.toml");

    let content = fs::read_to_string(&cargo_toml).expect(&format!("read {:?}", cargo_toml));

    toml::from_str(&content)
        .expect(&format!("parse toml {:?}", cargo_toml))
}

fn find_workspace_members(repo_root: &Path) -> Vec<String> {
    read_crate_manifest(repo_root).workspace.expect("workspace").members
}

#[derive(Debug)]
struct ParsedDependency {
    name: String,
    path: String,
}

#[derive(Debug)]
struct ParsedMember {
    /// Package name
    name: String,
    /// Path in the workspace
    member: String,
    /// Internal dep package names
    internal_deps: Vec<ParsedDependency>,
}

fn topo_sort(members: &mut [ParsedMember]) {
    'again: loop {
        for i in 0..members.len() {
            for j in i..members.len() {
                if i == j {
                    continue;
                }

                if members[i].internal_deps.iter().any(|d| d.name == members[j].name) {
                    members.swap(i, j);
                    // Native algorithm
                    continue 'again;
                }
            }
        }
        break;
    }
}

fn patch_crate(repo_root: &Path, member: &ParsedMember, new_version: &str) {
    let mut manifest_path = repo_root.to_owned();
    manifest_path.push(&member.member);
    manifest_path.push("Cargo.toml");

    let read = File::open(&manifest_path).expect(&format!("open manifest {:?}", manifest_path));
    let read = BufReader::new(read);

    #[derive(Debug, Eq, PartialEq)]
    enum WhereWeAre {
        Init,
        Package,
        Dependencies,
        Other,
    }

    let mut where_we_are = WhereWeAre::Init;

    let mut output = String::new();

    let mut version_patched = false;
    let mut internal_deps_seen = HashSet::new();

    for line in read.lines() {
        let line = line.expect("line");

        let mut line_written = false;

        if line == "[package]" {
            where_we_are = WhereWeAre::Package;
        } else if line == "[dependencies]" {
            where_we_are = WhereWeAre::Dependencies;
        } else if line.starts_with("[") {
            where_we_are = WhereWeAre::Other;
        } else {
            match where_we_are {
                WhereWeAre::Package => {
                    if line.starts_with("version =") {
                        assert!(!version_patched, "in {}", member.name);
                        version_patched = true;

                        writeln!(output, "version = \"{}\"", new_version).expect("write");
                        line_written = true;
                    }
                }
                WhereWeAre::Dependencies => {
                    for dep in &member.internal_deps {
                        if line.starts_with(&format!("{} ", dep.name)) {
                            let inserted = internal_deps_seen.insert(dep.name.clone());
                            assert!(inserted, "dep more than once in {}", member.name);

                            writeln!(
                                output,
                                "{} = {{ path = \"{}\", version = \"={}\" }}",
                                dep.name, dep.path, new_version)
                                .expect("write");
                            line_written = true;
                            break;
                        }
                    }
                }
                _ => {}
            }
        }

        if !line_written {
            writeln!(output, "{}", line).expect("write");
        }
    }

    assert!(version_patched, "in {}", member.name);
    assert_eq!(member.internal_deps.len(), internal_deps_seen.len(), "in {}", member.name);

    fs::write(&manifest_path, &output).expect("write patched manifest back");
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    assert_eq!(1, args.len());
    let new_version = &args[0];

    let repo_root = find_repo_root();

    let members = find_workspace_members(&repo_root);

    let mut publish_members = Vec::new();

    for member in members {
        let mut crate_path = repo_root.to_owned();
        crate_path.push(&member);
        let manifest = read_crate_manifest(&crate_path);
        let package = manifest.package
            .expect(&format!("package in {}", member));
        let dependencies = manifest.dependencies
            .expect(&format!("dependencies in {}", member));
        if package.publish == Some(false) {
            continue;
        }
        if package.version == "0.0.0" {
            panic!("version must be 0.0.0 is !publish for {}", member);
        }
        if package.version == "0.1.0" {
            panic!("default version 1.0.0 in {}", member);
        }

        let name = package.name;

        let mut internal_deps = Vec::new();

        for (name, dep) in dependencies {
            if let TomlDependency::Detailed(d) = dep {
                let path = match d.path {
                    Some(path) => path,
                    None => {
                        // Not internal dependency
                        continue
                    },
                };
                let version = d.version.expect(&format!("version in dep of {}", member));
                assert!(version.starts_with("="), "dep version must start with =: {}", member);
                internal_deps.push(ParsedDependency {
                    name,
                    path,
                });
            }
        }

        publish_members.push(ParsedMember {
            name,
            member,
            internal_deps,
        });
    }

    topo_sort(&mut publish_members);

    for member in &publish_members {
        patch_crate(&repo_root, member, new_version);
    }

    for member in &publish_members {
        println!("cargo publish --manifest-path={}/Cargo.toml", member.member);
    }
}
<commit_msg>Don't forget to git tag<commit_after>use std::env;
use std::path::PathBuf;
use std::fs;
use std::path::Path;
use std::collections::BTreeMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::BufReader;
use std::io::BufRead;
use std::fmt::Write as fmt_Write;

extern crate tempdir;
extern crate regex;

extern crate toml;

extern crate serde;
#[macro_use]
extern crate serde_derive;

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
struct TomlWorkspace {
    members: Vec<String>,
}

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
struct TomlPackage {
    name: String,
    version: String,
    publish: Option<bool>,
}

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
#[serde(rename_all = "kebab-case")]
pub struct DetailedTomlDependency {
    version: Option<String>,
    path: Option<String>
}

#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(untagged)]
pub enum TomlDependency {
    Simple(String),
    Detailed(DetailedTomlDependency),
}

#[derive(Deserialize, Serialize, Clone, Debug, Default)]
struct TomlManifest {
    package: Option<TomlPackage>,
    workspace: Option<TomlWorkspace>,
    dependencies: Option<BTreeMap<String, TomlDependency>>,
}

fn find_repo_root() -> PathBuf {
    let candidates = &[".", ".."];
    for dir in candidates {
        let dir = Path::new(dir).to_owned();
        let mut file = dir.clone();
        file.push("CHANGELOG.md");
        if file.exists() {
            return dir;
        }
    }
    panic!("cannot find repository root");
}

/// Read `Cargo.toml` from specified directory.
fn read_crate_manifest(crate_path: &Path) -> TomlManifest {
    assert!(crate_path.is_dir());

    let mut cargo_toml = crate_path.to_owned();
    cargo_toml.push("Cargo.toml");

    let content = fs::read_to_string(&cargo_toml).expect(&format!("read {:?}", cargo_toml));

    toml::from_str(&content)
        .expect(&format!("parse toml {:?}", cargo_toml))
}

fn find_workspace_members(repo_root: &Path) -> Vec<String> {
    read_crate_manifest(repo_root).workspace.expect("workspace").members
}

#[derive(Debug)]
struct ParsedDependency {
    name: String,
    path: String,
}

#[derive(Debug)]
struct ParsedMember {
    /// Package name
    name: String,
    /// Path in the workspace
    member: String,
    /// Internal dep package names
    internal_deps: Vec<ParsedDependency>,
}

fn topo_sort(members: &mut [ParsedMember]) {
    'again: loop {
        for i in 0..members.len() {
            for j in i..members.len() {
                if i == j {
                    continue;
                }

                if members[i].internal_deps.iter().any(|d| d.name == members[j].name) {
                    members.swap(i, j);
                    // Native algorithm
                    continue 'again;
                }
            }
        }
        break;
    }
}

fn patch_crate(repo_root: &Path, member: &ParsedMember, new_version: &str) {
    let mut manifest_path = repo_root.to_owned();
    manifest_path.push(&member.member);
    manifest_path.push("Cargo.toml");

    let read = File::open(&manifest_path).expect(&format!("open manifest {:?}", manifest_path));
    let read = BufReader::new(read);

    #[derive(Debug, Eq, PartialEq)]
    enum WhereWeAre {
        Init,
        Package,
        Dependencies,
        Other,
    }

    let mut where_we_are = WhereWeAre::Init;

    let mut output = String::new();

    let mut version_patched = false;
    let mut internal_deps_seen = HashSet::new();

    for line in read.lines() {
        let line = line.expect("line");

        let mut line_written = false;

        if line == "[package]" {
            where_we_are = WhereWeAre::Package;
        } else if line == "[dependencies]" {
            where_we_are = WhereWeAre::Dependencies;
        } else if line.starts_with("[") {
            where_we_are = WhereWeAre::Other;
        } else {
            match where_we_are {
                WhereWeAre::Package => {
                    if line.starts_with("version =") {
                        assert!(!version_patched, "in {}", member.name);
                        version_patched = true;

                        writeln!(output, "version = \"{}\"", new_version).expect("write");
                        line_written = true;
                    }
                }
                WhereWeAre::Dependencies => {
                    for dep in &member.internal_deps {
                        if line.starts_with(&format!("{} ", dep.name)) {
                            let inserted = internal_deps_seen.insert(dep.name.clone());
                            assert!(inserted, "dep more than once in {}", member.name);

                            writeln!(
                                output,
                                "{} = {{ path = \"{}\", version = \"={}\" }}",
                                dep.name, dep.path, new_version)
                                .expect("write");
                            line_written = true;
                            break;
                        }
                    }
                }
                _ => {}
            }
        }

        if !line_written {
            writeln!(output, "{}", line).expect("write");
        }
    }

    assert!(version_patched, "in {}", member.name);
    assert_eq!(member.internal_deps.len(), internal_deps_seen.len(), "in {}", member.name);

    fs::write(&manifest_path, &output).expect("write patched manifest back");
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    assert_eq!(1, args.len());
    let new_version = &args[0];

    let repo_root = find_repo_root();

    let members = find_workspace_members(&repo_root);

    let mut publish_members = Vec::new();

    for member in members {
        let mut crate_path = repo_root.to_owned();
        crate_path.push(&member);
        let manifest = read_crate_manifest(&crate_path);
        let package = manifest.package
            .expect(&format!("package in {}", member));
        let dependencies = manifest.dependencies
            .expect(&format!("dependencies in {}", member));
        if package.publish == Some(false) {
            continue;
        }
        if package.version == "0.0.0" {
            panic!("version must be 0.0.0 is !publish for {}", member);
        }
        if package.version == "0.1.0" {
            panic!("default version 1.0.0 in {}", member);
        }

        let name = package.name;

        let mut internal_deps = Vec::new();

        for (name, dep) in dependencies {
            if let TomlDependency::Detailed(d) = dep {
                let path = match d.path {
                    Some(path) => path,
                    None => {
                        // Not internal dependency
                        continue
                    },
                };
                let version = d.version.expect(&format!("version in dep of {}", member));
                assert!(version.starts_with("="), "dep version must start with =: {}", member);
                internal_deps.push(ParsedDependency {
                    name,
                    path,
                });
            }
        }

        publish_members.push(ParsedMember {
            name,
            member,
            internal_deps,
        });
    }

    topo_sort(&mut publish_members);

    for member in &publish_members {
        patch_crate(&repo_root, member, new_version);
    }

    for member in &publish_members {
        println!("cargo publish --manifest-path={}/Cargo.toml", member.member);
    }
    println!("git tag v{}", new_version);
}
<|endoftext|>


<commit_before>use rusqlite::Connection;
use std::env;
use std::fs::create_dir_all;
use ::input::*;
use std::collections::HashSet;
use output::SearchResult;
use std::process;

/// A struct to represent a movie to be attached to actors or directors
#[derive(Debug)]
pub struct Movie {
    pub title: String,
    pub year: String,
}

#[derive(Debug)]
struct Res {
    pub field: i64,
}

#[derive(Debug)]
struct GenreResult {
    pub field: String,
}

/// Checks if a database already exists
pub fn db_exists() -> bool {
    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");
    path_buf.push("database");
    path_buf.set_extension("db");
    path_buf.as_path().exists()
}

/// Returns a connection to existing database
fn get_connection() -> Connection {
    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");
    path_buf.push("database");
    path_buf.set_extension("db");

    let conn = Connection::open(path_buf).unwrap();
    conn
}

/// Creates a database with movies and related information for movie suggestions
/// Movies: movie titles with associated release date and genre
/// Actors: names of all actors
/// Rankings: associates movie ID with IMDb ratings and number of ratings
/// Crew (actors and directors): associates actors/directors with their movies
pub fn create_database() {

    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");

    // creates path if not existing
    if create_dir_all(&path_buf).is_err() {
        println!("Error while creating the Database at {:?}", path_buf);
        process::exit(1);
    }

    path_buf.push("database");
    path_buf.set_extension("db");

    let conn = Connection::open(path_buf).unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS movies (
                  id              INTEGER PRIMARY KEY,
                  title           TEXT NOT NULL,
                  year            TEXT NOT NULL,
                  rating          TEXT NOT NULL,
                  number          TEXT NOT NULL
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS genres (
                  movie_id        INTEGER NOT NULL,
                  genre           TEXT NOT NULL
            )",
                 &[])
        .unwrap();
}

/// List of movies with their year of release
pub fn import_movie(title: &str, year: &str, rating: &str, number: &str) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO movies (title, year, rating, number) VALUES ('{}', '{}', \
                           '{}', '{}')",
                          title.replace("'", "''").as_str(),
                          year,
                          rating,
                          number),
                 &[])
        .unwrap();
}

/// Genre information is added to the movie list
pub fn add_genres(title: &str, year: &str, genre: &str) {
    let conn = get_connection();

    let mut stm = conn.prepare(&format!("SELECT id FROM movies WHERE title = '{}' AND year = '{}'",
                          title.replace("'", "''").as_str(),
                          year))
        .unwrap();

    let res = stm.query_map(&[], |x| Res { field: x.get(0) })
        .unwrap();

    let mut existent = false;
    let mut movie_id = 0;

    for tmp in res {
        existent = true;
        movie_id = tmp.unwrap().field;
    }

    if existent {
        conn.execute(&format!("INSERT INTO genres (movie_id, genre) VALUES ('{}', '{}')",
                              movie_id,
                              genre),
                     &[])
            .unwrap();
    }
}

pub fn execute(search_params: SearchParams) -> Vec<SearchResult> {
    let conn = get_connection();
    let mut query = String::new();

    query.push_str("SELECT * FROM movies JOIN (SELECT movie_id FROM genres JOIN (SELECT movie_id \
                    as m_id, COUNT(movie_id) AS ctr FROM (SELECT * FROM genres WHERE ");

    let mut genres_string = String::new();
    let mut genres: HashSet<String> = HashSet::new();

    if !search_params.get_movies().is_empty() {
        let mut movie_query = String::new();
        movie_query.push_str("SELECT genre FROM movies JOIN genres ON id = movie_id WHERE");

        let mut tmp = String::new();

        for movie in search_params.get_movies() {
            if tmp.is_empty() {
                tmp.push_str(format!(" title = '{}'", movie.replace("'", "''")).as_str());
            } else {
                tmp.push_str(format!(" OR title = '{}'", movie.replace("'", "''")).as_str());
            }
        }

        movie_query.push_str(tmp.as_str());

        let mut stm = conn.prepare(movie_query.as_str()).unwrap();

        let res = stm.query_map(&[], |x| GenreResult { field: x.get(0) }).unwrap();

        for genre in res {
            genres.insert(genre.unwrap().field);
        }
    }

    for genre in search_params.get_genres() {
        genres.insert(genre);
    }

    for genre in &genres {
        if genres_string.is_empty() {
            genres_string.push_str(format!("genre = '{}'", genre).as_str());
        } else {
            genres_string.push_str(format!(" OR genre = '{}'", genre).as_str());
        }
    }

    query.push_str(genres_string.as_str());

    query.push_str(format!(") GROUP BY m_id) ON movie_id = m_id WHERE ctr >= {} GROUP BY \
                            movie_id) ON id = movie_id WHERE rating > {} ORDER BY rating DESC",
                           genres.len(),
                           search_params.get_rating())
        .as_str());

    let mut stm = conn.prepare(query.as_str()).unwrap();
    let res = stm.query_map(&[], |x| {
        SearchResult {
            title: x.get(1),
            score: x.get(3),
            number: x.get(4),
            year: x.get(2),
            genres: Vec::new(),
        }
    });

    let mut tmp = Vec::new();
    for movie in res.unwrap() {
        let mut mov = movie.unwrap().clone();

        let mut stm =
            conn.prepare(format!("SELECT genre FROM genres WHERE movie_id = (SELECT id FROM \
                                  movies WHERE title = '{}' AND year = '{}') GROUP BY genre \
                                  ORDER BY genre ASC",
                                 mov.title,
                                 mov.year)
                    .as_str())
                .unwrap();
        let genres = stm.query_map(&[], |x| GenreResult { field: x.get(0) }).unwrap();
        let mut genre = Vec::new();
        for g in genres {
            genre.push(g.unwrap().field);
        }
        mov.genres = genre;
        tmp.push(mov);
    }

    tmp

}
<commit_msg>fixed issue<commit_after>use rusqlite::Connection;
use std::env;
use std::fs::create_dir_all;
use ::input::*;
use std::collections::HashSet;
use output::SearchResult;
use std::process;

/// A struct to represent a movie to be attached to actors or directors
#[derive(Debug)]
pub struct Movie {
    pub title: String,
    pub year: String,
}

#[derive(Debug)]
struct Res {
    pub field: i64,
}

#[derive(Debug)]
struct GenreResult {
    pub field: String,
}

/// Checks if a database already exists
pub fn db_exists() -> bool {
    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");
    path_buf.push("database");
    path_buf.set_extension("db");
    path_buf.as_path().exists()
}

/// Returns a connection to existing database
fn get_connection() -> Connection {
    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");
    path_buf.push("database");
    path_buf.set_extension("db");

    let conn = Connection::open(path_buf).unwrap();
    conn
}

/// Creates a database with movies and related information for movie suggestions
/// Movies: movie titles with associated release date and genre
/// Actors: names of all actors
/// Rankings: associates movie ID with IMDb ratings and number of ratings
/// Crew (actors and directors): associates actors/directors with their movies
pub fn create_database() {

    let tmp = env::home_dir();
    let mut path_buf = tmp.unwrap();
    path_buf.push("Film-O-Mat");

    // creates path if not existing
    if create_dir_all(&path_buf).is_err() {
        println!("Error while creating the Database at {:?}", path_buf);
        process::exit(1);
    }

    path_buf.push("database");
    path_buf.set_extension("db");

    let conn = Connection::open(path_buf).unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS movies (
                  id              INTEGER PRIMARY KEY,
                  title           TEXT NOT NULL,
                  year            TEXT NOT NULL,
                  rating          TEXT NOT NULL,
                  number          TEXT NOT NULL
                  )",
                 &[])
        .unwrap();

    conn.execute("CREATE TABLE IF NOT EXISTS genres (
                  movie_id        INTEGER NOT NULL,
                  genre           TEXT NOT NULL
            )",
                 &[])
        .unwrap();
}

/// List of movies with their year of release
pub fn import_movie(title: &str, year: &str, rating: &str, number: &str) {
    let conn = get_connection();

    conn.execute(&format!("INSERT INTO movies (title, year, rating, number) VALUES ('{}', '{}', \
                           '{}', '{}')",
                          title.replace("'", "''").as_str(),
                          year,
                          rating,
                          number),
                 &[])
        .unwrap();
}

/// Genre information is added to the movie list
pub fn add_genres(title: &str, year: &str, genre: &str) {
    let conn = get_connection();

    let mut stm = conn.prepare(&format!("SELECT id FROM movies WHERE title = '{}' AND year = '{}'",
                          title.replace("'", "''").as_str(),
                          year))
        .unwrap();

    let res = stm.query_map(&[], |x| Res { field: x.get(0) })
        .unwrap();

    let mut existent = false;
    let mut movie_id = 0;

    for tmp in res {
        existent = true;
        movie_id = tmp.unwrap().field;
    }

    if existent {
        conn.execute(&format!("INSERT INTO genres (movie_id, genre) VALUES ('{}', '{}')",
                              movie_id,
                              genre),
                     &[])
            .unwrap();
    }
}

pub fn execute(search_params: SearchParams) -> Vec<SearchResult> {
    let conn = get_connection();
    let mut query = String::new();

    query.push_str("SELECT * FROM movies JOIN (SELECT movie_id FROM genres JOIN (SELECT movie_id \
                    as m_id, COUNT(movie_id) AS ctr FROM (SELECT * FROM genres WHERE ");

    let mut genres_string = String::new();
    let mut genres: HashSet<String> = HashSet::new();

    if !search_params.get_movies().is_empty() {
        let mut movie_query = String::new();
        movie_query.push_str("SELECT genre FROM movies JOIN genres ON id = movie_id WHERE");

        let mut tmp = String::new();

        for movie in search_params.get_movies() {
            if tmp.is_empty() {
                tmp.push_str(format!(" title = '{}'", movie.replace("'", "''")).as_str());
            } else {
                tmp.push_str(format!(" OR title = '{}'", movie.replace("'", "''")).as_str());
            }
        }

        movie_query.push_str(tmp.as_str());

        let mut stm = conn.prepare(movie_query.as_str()).unwrap();

        let res = stm.query_map(&[], |x| GenreResult { field: x.get(0) }).unwrap();

        for genre in res {
            genres.insert(genre.unwrap().field);
        }
    }

    for genre in search_params.get_genres() {
        genres.insert(genre);
    }

    for genre in &genres {
        if genres_string.is_empty() {
            genres_string.push_str(format!("genre = '{}'", genre).as_str());
        } else {
            genres_string.push_str(format!(" OR genre = '{}'", genre).as_str());
        }
    }

    query.push_str(genres_string.as_str());

    query.push_str(format!(") GROUP BY m_id) ON movie_id = m_id WHERE ctr >= {} GROUP BY \
                            movie_id) ON id = movie_id WHERE rating > {} ORDER BY rating DESC",
                           genres.len(),
                           search_params.get_rating())
        .as_str());

    let mut stm = conn.prepare(query.as_str()).unwrap();
    let res = stm.query_map(&[], |x| {
        SearchResult {
            title: x.get(1),
            score: x.get(3),
            number: x.get(4),
            year: x.get(2),
            genres: Vec::new(),
        }
    });

    let mut tmp = Vec::new();
    for movie in res.unwrap() {
        let mut mov = movie.unwrap().clone();

        let mut stm =
            conn.prepare(format!("SELECT genre FROM genres WHERE movie_id = (SELECT id FROM \
                                  movies WHERE title = '{}' AND year = '{}') GROUP BY genre \
                                  ORDER BY genre ASC",
                                 mov.title.replace("'", "''"),
                                 mov.year)
                    .as_str())
                .unwrap();
        let genres = stm.query_map(&[], |x| GenreResult { field: x.get(0) }).unwrap();
        let mut genre = Vec::new();
        for g in genres {
            genre.push(g.unwrap().field);
        }
        println!("{:?}", genre);
        mov.genres = genre;
        tmp.push(mov);
    }

    tmp

}
<|endoftext|>


<commit_before>//
// url.rs - module for url-encoding
//

import vec::len;

export query_escape, query_unescape;

enum enc_mode {
    query,
    path,
    fragment,
    userinfo
}

fn query_escape(s: str) -> str { url_escape(s, query) }
fn query_unescape(s: str) -> str { url_unescape(s, query) }

#[inline(always)]
fn ishex(c: u8) -> bool {
    if 48u8 <= c && c <= 57u8 { true }
    else if 65u8 <= c && c <= 90u8  { true }
    else if 97u8 <= c && c <= 122u8 { true }
    else { false }
}

#[inline(always)]
fn unhex(c: u8) -> u8 {
    if 48u8 <= c && c <= 57u8 { c - 48u8 }
    else if 65u8 <= c && c <= 90u8  { c - 65u8 + 10u8 }
    else if 97u8 <= c && c <= 122u8 { c - 97u8 + 10u8 }
    else { fail "should be unreachable"; }
}

fn should_escape(c: u8, mode: enc_mode) -> bool {
    alt c {
      65u8 to 90u8  { ret false; } // A .. Z
      97u8 to 122u8 { ret false; } // a .. z
      48u8 to 57u8  { ret false; } // 0 .. 9
      _ { }
    }

    alt c {
      45u8  | 95u8 | 46u8 | 33u8 | // '-', '_', '.', '!'
      126u8 | 42u8 | 39u8 | 40u8 | // '~', '*', '\'', '('
      41u8 { false }               // ')'

      36u8 | 38u8 | 43u8 | 44u8 |  // '$', '&', '+', ','
      47u8 | 58u8 | 59u8 | 61u8 |  // '/', ':', ';', '='
      63u8 | 64u8 {                // '?', '@'
        alt mode {
          query    { true }
          fragment { false }
          path     { (c == 63u8) }
          userinfo { (c == 47u8 || c == 58u8 || c == 64u8) }
        }
      }

      _ { true }
    }
}

fn url_escape(s: str, mode: enc_mode) -> str {
    let bs = str::bytes(s);
    let src_length = len(bs);
    let mut space_count = 0u;
    let mut hex_count = 0u;
    let table =
        [48u8, 49u8, 50u8, 51u8, 52u8, 53u8, 54u8, 55u8,
         56u8, 57u8, 65u8, 66u8, 67u8, 68u8, 69u8, 70u8];

    uint::range(0u, src_length) {|i|
        let c = bs[i];
        if should_escape(bs[i], mode) {
            if c == 32u8 && mode == query {
                space_count += 1u;
            } else {
                hex_count += 1u;
            }
        }
    }

    // Nothing to do if there's no space and no escapable chars in `s`
    if space_count == 0u && hex_count == 0u {
        ret s;
    }

    let ts = vec::to_mut(vec::from_elem(src_length + 2u * hex_count, 0u8));
    let mut i = 0u;
    let mut j = 0u;
    let mut c = 0u8;

    while i < src_length {
        c = bs[i];
        if c == 32u8 && mode == query {
            ts[j] = '+' as u8;
            j += 1u;
        } else if should_escape(c, mode) {
            ts[j] = '%' as u8;
            ts[j+1u] = table[c >> 4u8];
            ts[j+2u] = table[c & 0x0f_u8];
            j += 3u;
        } else {
            ts[j] = c;
            j += 1u;
        }
        i += 1u;
    }

    str::from_bytes(vec::from_mut(ts))
}

fn url_unescape(s: str, mode: enc_mode) -> str {
    let bs = str::bytes(s);
    let src_length = len(bs);
    let mut n = 0u;
    let mut i = 0u;
    let mut hasplus = false;

    while i < src_length {
        let c = bs[i];
        if c == 37u8 {
            n += 1u;
            if i+2u >= src_length || !ishex(bs[i+1u]) || !ishex(bs[i+2u]) {
                fail #fmt["Invalid URL escape: '%s'", s];
            }
            i += 3u;
        } else if c == 43u8 {
            hasplus = mode == query;
            i += 1u;
        } else {
            i += 1u;
        }
    }

    if n == 0u && !hasplus {
        ret s;
    }

    let ts = vec::to_mut(vec::from_elem(src_length - 2u * n, 0u8));
    let mut i = 0u;
    let mut j = 0u;

    while i < src_length {
        let c = bs[i];
        if c == 37u8 {
            ts[j] = (unhex(bs[i+1u])<<4u) | unhex(bs[i+2u]);
            j += 1u;
            i += 3u;
        } else if c == 43u8 {
            if mode == query { ts[j] = 32u8; }
            else { ts[j] = 43u8; }
            j += 1u;
            i += 1u;
        } else {
            ts[j] = c;
            j += 1u;
            i += 1u;
        }
    }

    str::from_bytes(vec::from_mut(ts))
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_should_escape() {
        assert should_escape('A' as u8, path) == false;
        assert should_escape('0' as u8, path) == false;
        assert should_escape('?' as u8, path) == true;
        assert should_escape('A' as u8, query) == false;
        assert should_escape('0' as u8, query) == false;
        assert should_escape('@' as u8, query) == true;
        assert should_escape('/' as u8, userinfo) == true;
        assert should_escape(':' as u8, userinfo) == true;
        assert should_escape('$' as u8, userinfo) == false;
        assert should_escape('A' as u8, fragment) == false;
        assert should_escape('0' as u8, fragment) == false;
        assert should_escape(';' as u8, fragment) == false;
    }
    #[test]
    fn test_query_escape() {
        assert query_escape("a") == "a";
        assert query_escape("a z") == "a+z";
        assert query_escape("å") == "%C3%A5";
        assert query_escape("?") == "%3F";
        assert query_escape("あ") == "%E3%81%82";
    }
    #[test]
    fn test_query_unescape() {
        assert query_unescape("a") == "a";
        assert query_unescape("a+z") == "a z";
        assert query_unescape("%3f") == "?";
        assert query_unescape("%C3%A5") == "å";
        assert query_unescape("%E3%81%82") == "あ";
    }
}
<commit_msg>url: fn -> pure fn<commit_after>//
// url.rs - module for url-encoding
//

import vec::len;

export query_escape, query_unescape;

enum enc_mode {
    query,
    path,
    fragment,
    userinfo
}

fn query_escape(s: str) -> str { url_escape(s, query) }
fn query_unescape(s: str) -> str { url_unescape(s, query) }

#[inline(always)]
pure fn ishex(c: u8) -> bool {
    if 48u8 <= c && c <= 57u8 { true }
    else if 65u8 <= c && c <= 90u8  { true }
    else if 97u8 <= c && c <= 122u8 { true }
    else { false }
}

#[inline(always)]
pure fn unhex(c: u8) -> u8 {
    if 48u8 <= c && c <= 57u8 { c - 48u8 }
    else if 65u8 <= c && c <= 90u8  { c - 65u8 + 10u8 }
    else if 97u8 <= c && c <= 122u8 { c - 97u8 + 10u8 }
    else { fail "should be unreachable"; }
}

pure fn should_escape(c: u8, mode: enc_mode) -> bool {
    alt c {
      65u8 to 90u8  { ret false; } // A .. Z
      97u8 to 122u8 { ret false; } // a .. z
      48u8 to 57u8  { ret false; } // 0 .. 9
      _ { }
    }

    alt c {
      45u8  | 95u8 | 46u8 | 33u8 | // '-', '_', '.', '!'
      126u8 | 42u8 | 39u8 | 40u8 | // '~', '*', '\'', '('
      41u8 { false }               // ')'

      36u8 | 38u8 | 43u8 | 44u8 |  // '$', '&', '+', ','
      47u8 | 58u8 | 59u8 | 61u8 |  // '/', ':', ';', '='
      63u8 | 64u8 {                // '?', '@'
        alt mode {
          query    { true }
          fragment { false }
          path     { (c == 63u8) }
          userinfo { (c == 47u8 || c == 58u8 || c == 64u8) }
        }
      }

      _ { true }
    }
}

fn url_escape(s: str, mode: enc_mode) -> str {
    let bs = str::bytes(s);
    let src_length = len(bs);
    let mut space_count = 0u;
    let mut hex_count = 0u;
    let table =
        [48u8, 49u8, 50u8, 51u8, 52u8, 53u8, 54u8, 55u8,
         56u8, 57u8, 65u8, 66u8, 67u8, 68u8, 69u8, 70u8];

    uint::range(0u, src_length) {|i|
        let c = bs[i];
        if should_escape(bs[i], mode) {
            if c == 32u8 && mode == query {
                space_count += 1u;
            } else {
                hex_count += 1u;
            }
        }
    }

    // Nothing to do if there's no space and no escapable chars in `s`
    if space_count == 0u && hex_count == 0u {
        ret s;
    }

    let ts = vec::to_mut(vec::from_elem(src_length + 2u * hex_count, 0u8));
    let mut i = 0u;
    let mut j = 0u;
    let mut c = 0u8;

    while i < src_length {
        c = bs[i];
        if c == 32u8 && mode == query {
            ts[j] = '+' as u8;
            j += 1u;
        } else if should_escape(c, mode) {
            ts[j] = '%' as u8;
            ts[j+1u] = table[c >> 4u8];
            ts[j+2u] = table[c & 0x0f_u8];
            j += 3u;
        } else {
            ts[j] = c;
            j += 1u;
        }
        i += 1u;
    }

    str::from_bytes(vec::from_mut(ts))
}

fn url_unescape(s: str, mode: enc_mode) -> str {
    let bs = str::bytes(s);
    let src_length = len(bs);
    let mut n = 0u;
    let mut i = 0u;
    let mut hasplus = false;

    while i < src_length {
        let c = bs[i];
        if c == 37u8 {
            n += 1u;
            if i+2u >= src_length || !ishex(bs[i+1u]) || !ishex(bs[i+2u]) {
                fail #fmt["Invalid URL escape: '%s'", s];
            }
            i += 3u;
        } else if c == 43u8 {
            hasplus = mode == query;
            i += 1u;
        } else {
            i += 1u;
        }
    }

    if n == 0u && !hasplus {
        ret s;
    }

    let ts = vec::to_mut(vec::from_elem(src_length - 2u * n, 0u8));
    let mut i = 0u;
    let mut j = 0u;

    while i < src_length {
        let c = bs[i];
        if c == 37u8 {
            ts[j] = (unhex(bs[i+1u])<<4u) | unhex(bs[i+2u]);
            j += 1u;
            i += 3u;
        } else if c == 43u8 {
            if mode == query { ts[j] = 32u8; }
            else { ts[j] = 43u8; }
            j += 1u;
            i += 1u;
        } else {
            ts[j] = c;
            j += 1u;
            i += 1u;
        }
    }

    str::from_bytes(vec::from_mut(ts))
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_should_escape() {
        assert should_escape('A' as u8, path) == false;
        assert should_escape('0' as u8, path) == false;
        assert should_escape('?' as u8, path) == true;
        assert should_escape('A' as u8, query) == false;
        assert should_escape('0' as u8, query) == false;
        assert should_escape('@' as u8, query) == true;
        assert should_escape('/' as u8, userinfo) == true;
        assert should_escape(':' as u8, userinfo) == true;
        assert should_escape('$' as u8, userinfo) == false;
        assert should_escape('A' as u8, fragment) == false;
        assert should_escape('0' as u8, fragment) == false;
        assert should_escape(';' as u8, fragment) == false;
    }
    #[test]
    fn test_query_escape() {
        assert query_escape("a") == "a";
        assert query_escape("a z") == "a+z";
        assert query_escape("å") == "%C3%A5";
        assert query_escape("?") == "%3F";
        assert query_escape("あ") == "%E3%81%82";
    }
    #[test]
    fn test_query_unescape() {
        assert query_unescape("a") == "a";
        assert query_unescape("a+z") == "a z";
        assert query_unescape("%3f") == "?";
        assert query_unescape("%C3%A5") == "å";
        assert query_unescape("%E3%81%82") == "あ";
    }
}
<|endoftext|>


<commit_before>//! Simple websocket client.

#![allow(unused_variables)]
extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate futures;
extern crate tokio_core;

use std::{io, thread};
use std::time::Duration;

use actix::*;
use futures::Future;
use tokio_core::net::TcpStream;
use actix_web::ws::{client, Message, WsClientError};

fn main() {
    ::std::env::set_var("RUST_LOG", "actix_web=info");
    let _ = env_logger::init();
    let sys = actix::System::new("ws-example");

    Arbiter::handle().spawn(
        client::WsClient::new("http://127.0.0.1:8080/ws/")
            .connect().unwrap()
            .map_err(|e| {
                println!("Error: {}", e);
                ()
            })
            .map(|(reader, writer)| {
                let addr: SyncAddress<_> = ChatClient::create(|ctx| {
                    ChatClient::add_stream(reader, ctx);
                    ChatClient(writer)
                });

                // start console loop
                thread::spawn(move|| {
                    loop {
                        let mut cmd = String::new();
                        if io::stdin().read_line(&mut cmd).is_err() {
                            println!("error");
                            return
                        }
                        addr.send(ClientCommand(cmd));
                    }
                });

                ()
            })
    );

    let _ = sys.run();
}

struct ChatClient(client::WsWriter<TcpStream>);

#[derive(Message)]
struct ClientCommand(String);

impl Actor for ChatClient {
    type Context = Context<Self>;

    fn started(&mut self, ctx: &mut Context<Self>) {
        // start heartbeats otherwise server will disconnect after 10 seconds
        self.hb(ctx)
    }

    fn stopping(&mut self, _: &mut Context<Self>) -> bool {
        println!("Stopping");

        // Stop application on disconnect
        Arbiter::system().send(actix::msgs::SystemExit(0));

        true
    }
}

impl ChatClient {
    fn hb(&self, ctx: &mut Context<Self>) {
        ctx.run_later(Duration::new(1, 0), |act, ctx| {
            act.0.ping("");
            act.hb(ctx);
        });
    }
}

/// Handle stdin commands
impl Handler<ClientCommand> for ChatClient {
    type Result = ();

    fn handle(&mut self, msg: ClientCommand, ctx: &mut Context<Self>) {
        self.0.text(msg.0.as_str())
    }
}

/// Handle server websocket messages
impl StreamHandler<Message, WsClientError> for ChatClient {

    fn handle(&mut self, msg: Message, ctx: &mut Context<Self>) {
        match msg {
            Message::Text(txt) => println!("Server: {:?}", txt),
            _ => ()
        }
    }

    fn started(&mut self, ctx: &mut Context<Self>, _: SpawnHandle) {
        println!("Connected");
    }

    fn finished(&mut self, err: Option<WsClientError>, ctx: &mut Context<Self>, _: SpawnHandle) {
        println!("Server disconnected");
        ctx.stop()
    }
}
<commit_msg>update websocket example<commit_after>//! Simple websocket client.

#![allow(unused_variables)]
extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate futures;
extern crate tokio_core;

use std::{io, thread};
use std::time::Duration;

use actix::*;
use futures::Future;
use tokio_core::net::TcpStream;
use actix_web::ws::{client, Message, WsClientError};

fn main() {
    ::std::env::set_var("RUST_LOG", "actix_web=info");
    let _ = env_logger::init();
    let sys = actix::System::new("ws-example");

    Arbiter::handle().spawn(
        client::WsClient::new("http://127.0.0.1:8080/ws/")
            .connect().unwrap()
            .map_err(|e| {
                println!("Error: {}", e);
                ()
            })
            .map(|(reader, writer)| {
                let addr: SyncAddress<_> = ChatClient::create(|ctx| {
                    ChatClient::add_stream(reader, ctx);
                    ChatClient(writer)
                });

                // start console loop
                thread::spawn(move|| {
                    loop {
                        let mut cmd = String::new();
                        if io::stdin().read_line(&mut cmd).is_err() {
                            println!("error");
                            return
                        }
                        addr.send(ClientCommand(cmd));
                    }
                });

                ()
            })
    );

    let _ = sys.run();
}

struct ChatClient(client::WsWriter<TcpStream>);

#[derive(Message)]
struct ClientCommand(String);

impl Actor for ChatClient {
    type Context = Context<Self>;

    fn started(&mut self, ctx: &mut Context<Self>) {
        // start heartbeats otherwise server will disconnect after 10 seconds
        self.hb(ctx)
    }

    fn stopping(&mut self, _: &mut Context<Self>) -> bool {
        println!("Stopping");

        // Stop application on disconnect
        Arbiter::system().send(actix::msgs::SystemExit(0));

        true
    }
}

impl ChatClient {
    fn hb(&self, ctx: &mut Context<Self>) {
        ctx.run_later(Duration::new(1, 0), |act, ctx| {
            act.0.ping("");
            act.hb(ctx);
        });
    }
}

/// Handle stdin commands
impl Handler<ClientCommand> for ChatClient {
    type Result = ();

    fn handle(&mut self, msg: ClientCommand, ctx: &mut Context<Self>) {
        self.0.text(msg.0.as_str())
    }
}

/// Handle server websocket messages
impl StreamHandler<Message, WsClientError> for ChatClient {

    fn handle(&mut self, msg: Message, ctx: &mut Context<Self>) {
        match msg {
            Message::Text(txt) => println!("Server: {:?}", txt),
            _ => ()
        }
    }

    fn started(&mut self, ctx: &mut Context<Self>) {
        println!("Connected");
    }

    fn finished(&mut self, err: Option<WsClientError>, ctx: &mut Context<Self>) {
        println!("Server disconnected");
        ctx.stop()
    }
}
<|endoftext|>


<commit_before>use std::collections::HashSet;

pub fn is_pangram(s: &str) -> bool {
    let mut h = HashSet::new();
    for c in s.to_lowercase().chars().filter(|c| *c >= 'a' && *c <= 'z') {
        h.insert(c);
    }
    return 26 == h.len();
}
<commit_msg>cleaner<commit_after>use std::collections::HashSet;
use std::ascii::AsciiExt;

pub fn is_pangram(s: &str) -> bool {
    26 == s.to_lowercase()
           .chars()
           .filter(|c| c.is_ascii() && c.is_alphabetic())
           .collect::<HashSet<_>>()
           .len()
}
<|endoftext|>


<commit_before>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

//! Vector operations.

use num::traits::NumCast;
use num::complex::{Complex32, Complex64};
use vector::ops::{Copy, Axpy, Scal, Dot, Nrm2, Asum, Iamax};

pub mod ll;
pub mod ops;

/// Methods that allow a type to be used in BLAS functions as a vector.
pub trait Vector<T> {
    /// The stride within the vector. For example, if `inc` returns 7, every
    /// 7th element is used. Defaults to 1.
    fn inc(&self) -> i32 { 1 }
    /// The number of elements in the vector.
    fn len(&self) -> i32;
    /// An unsafe pointer to a contiguous block of memory.
    unsafe fn as_ptr(&self) -> *const T;
    /// An unsafe mutable pointer to a contiguous block of memory.
    unsafe fn as_mut_ptr(&mut self) -> *mut T;
}

impl<'a, T> Into<Vec<T>> for &'a Vector<T>
    where T: Copy {

    fn into(self) -> Vec<T> {
        let n = self.len() as usize;

        let mut x = Vec::with_capacity(n);
        unsafe { x.set_len(n); }
        Copy::copy(self, &mut x);

        x
    }
}

pub trait VectorOperations<T>: Sized + Vector<T>
    where T: Copy + Axpy + Scal + Dot + Nrm2 + Asum + Iamax {

    #[inline]
    fn update(&mut self, alpha: &T, x: &Vector<T>) -> &mut Self {
        Axpy::axpy(alpha, x, self);
        self
    }

    #[inline]
    fn scale(&mut self, alpha: &T) -> &mut Self {
        Scal::scal(alpha, self);
        self
    }

    #[inline]
    fn dot(&self, x: &Vector<T>) -> T {
        Dot::dot(self, x)
    }

    #[inline]
    fn abs_sum(&self) -> T {
        Asum::asum(self)
    }

    #[inline]
    fn norm(&self) -> T {
        Nrm2::nrm2(self)
    }

    #[inline]
    fn max_index(&self) -> usize {
        Iamax::iamax(self)
    }
}

impl<T> Vector<T> for Vec<T> {

    #[inline]
    fn len(&self) -> i32 {
        let l: Option<i32> = NumCast::from(Vec::len(self));
        match l {
            Some(l) => l,
            None => panic!(),
        }
    }

    #[inline]
    unsafe fn as_ptr(&self) -> *const T { self[..].as_ptr() }

    #[inline]
    unsafe fn as_mut_ptr(&mut self) -> *mut T { (&mut self[..]).as_mut_ptr() }
}

impl<'a, T> Vector<T> for &'a [T] {

    #[inline]
    fn len(&self) -> i32 {
        let l: Option<i32> = NumCast::from(<&[T]>::len(self));
        match l {
            Some(l) => l,
            None => panic!(),
        }
    }

    #[inline]
    unsafe fn as_ptr(&self) -> *const T { <&[T]>::as_ptr(self) }

    #[inline]
    unsafe fn as_mut_ptr(&mut self) -> *mut T { <&[T]>::as_mut_ptr(self) }
}

macro_rules! operations_impl(
    ($v: ident, $($t: ty), +) => (
        $( impl VectorOperations<$t> for $v<$t> {} )+
    )
);

operations_impl!(Vec, f32, f64, Complex32, Complex64);
impl<'a> VectorOperations<f32> for &'a [f32] {}
impl<'a> VectorOperations<f64> for &'a [f64] {}
impl<'a> VectorOperations<Complex32> for &'a [Complex32] {}
impl<'a> VectorOperations<Complex64> for &'a [Complex64] {}
<commit_msg>Fix method choice for recursion issue<commit_after>// Copyright 2014 Michael Yang. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

//! Vector operations.

use num::traits::NumCast;
use num::complex::{Complex32, Complex64};
use vector::ops::{Copy, Axpy, Scal, Dot, Nrm2, Asum, Iamax};

pub mod ll;
pub mod ops;

/// Methods that allow a type to be used in BLAS functions as a vector.
pub trait Vector<T> {
    /// The stride within the vector. For example, if `inc` returns 7, every
    /// 7th element is used. Defaults to 1.
    fn inc(&self) -> i32 { 1 }
    /// The number of elements in the vector.
    fn len(&self) -> i32;
    /// An unsafe pointer to a contiguous block of memory.
    unsafe fn as_ptr(&self) -> *const T;
    /// An unsafe mutable pointer to a contiguous block of memory.
    unsafe fn as_mut_ptr(&mut self) -> *mut T;
}

impl<'a, T> Into<Vec<T>> for &'a Vector<T>
    where T: Copy {

    fn into(self) -> Vec<T> {
        let n = self.len() as usize;

        let mut x = Vec::with_capacity(n);
        unsafe { x.set_len(n); }
        Copy::copy(self, &mut x);

        x
    }
}

pub trait VectorOperations<T>: Sized + Vector<T>
    where T: Copy + Axpy + Scal + Dot + Nrm2 + Asum + Iamax {

    #[inline]
    fn update(&mut self, alpha: &T, x: &Vector<T>) -> &mut Self {
        Axpy::axpy(alpha, x, self);
        self
    }

    #[inline]
    fn scale(&mut self, alpha: &T) -> &mut Self {
        Scal::scal(alpha, self);
        self
    }

    #[inline]
    fn dot(&self, x: &Vector<T>) -> T {
        Dot::dot(self, x)
    }

    #[inline]
    fn abs_sum(&self) -> T {
        Asum::asum(self)
    }

    #[inline]
    fn norm(&self) -> T {
        Nrm2::nrm2(self)
    }

    #[inline]
    fn max_index(&self) -> usize {
        Iamax::iamax(self)
    }
}

impl<T> Vector<T> for Vec<T> {

    #[inline]
    fn len(&self) -> i32 {
        let l: Option<i32> = NumCast::from(Vec::len(self));
        match l {
            Some(l) => l,
            None => panic!(),
        }
    }

    #[inline]
    unsafe fn as_ptr(&self) -> *const T { self[..].as_ptr() }

    #[inline]
    unsafe fn as_mut_ptr(&mut self) -> *mut T { (&mut self[..]).as_mut_ptr() }
}

impl<T> Vector<T> for [T] {

    #[inline]
    fn len(&self) -> i32 {
        let l: Option<i32> = NumCast::from(<[T]>::len(self));
        match l {
            Some(l) => l,
            None => panic!(),
        }
    }

    #[inline]
    unsafe fn as_ptr(&self) -> *const T { <[T]>::as_ptr(self) }

    #[inline]
    unsafe fn as_mut_ptr(&mut self) -> *mut T { <[T]>::as_mut_ptr(self) }
}

macro_rules! operations_impl(
    ($v: ident, $($t: ty), +) => (
        $( impl VectorOperations<$t> for $v<$t> {} )+
    )
);

operations_impl!(Vec, f32, f64, Complex32, Complex64);
//impl<'a> VectorOperations<f32> for &'a [f32] {}
//impl<'a> VectorOperations<f64> for &'a [f64] {}
//impl<'a> VectorOperations<Complex32> for &'a [Complex32] {}
//impl<'a> VectorOperations<Complex64> for &'a [Complex64] {}
<|endoftext|>


<commit_before>//! SemanticEngine implementation for [the racer library](https://github.com/phildawes/racer)
use std::fs::File;
use std::path::Path;
use std::io::{self, Read};

use engine::{SemanticEngine, Definition, Context, CursorPosition};

pub struct Racer;

use super::Result;

impl SemanticEngine for Racer {
    fn find_definition(&self, ctx: &Context) -> Result<Option<Definition>> {
        let path = ctx.query_path();

        let session = ::racer::core::Session::from_path(path, path);

        for buffer in ctx.buffers {
            session.cache_file_contents(buffer.path(), &buffer.contents[..]);
        }

        let query_src = &session.load_file(path).src.code[..];

        let pos = ::racer::scopes::coords_to_point(query_src,
                                                   ctx.query_cursor.line,
                                                   ctx.query_cursor.col);

        // TODO catch_panic: apparently this can panic! in a string operation. Something about pos
        // not landing on a character boundary.
        Ok(match ::racer::core::find_definition(query_src, path, pos, &session) {
            Some(m) => {
                // TODO modify racer Match to return line, col. For now, read the file it found a
                // match in to translate the Match position into line, col.
                let (line, col) = {
                    let found_src = &session.load_file(m.filepath.as_path());
                    let found_src_str = &found_src.src.code[..];
                    // TODO This can panic if the position is out of bounds :(
                    ::racer::scopes::point_to_coords(found_src_str, m.point)
                };

                let match_type = format!("{:?}", m.mtype);
                Some(Definition {
                    position: CursorPosition {
                        line: line,
                        col: col
                    },
                    dtype: match_type,
                    file_path: m.filepath.to_str().unwrap().to_string(),
                    text: m.matchstr.clone(),
                    text_context: m.contextstr.clone(),
                })
            },
            None => None
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use engine::{Context, CursorPosition, SemanticEngine, Buffer};

    use ::util::fs::TmpFile;

    #[test]
    #[allow(unused_variables)]
    fn find_definition() {
        // FIXME this is just here for side effects.
        let src2 = TmpFile::with_name("src2.rs", "
            pub fn myfn() {}
            pub fn foo() {}
            ");

        let src = "
            use src2::*;
            mod src2;
            fn main() {
                myfn();
            }";

        let mut buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string()
        }];

        let ctx = Context::new(&buffers, CursorPosition { line: 5, col: 17 }, "src.rs");
        let racer = Racer;
        let def = racer.find_definition(&ctx).unwrap().unwrap();

        assert_eq!(def.text, "myfn");
    }
}
<commit_msg>Fix some compiler warnings<commit_after>//! SemanticEngine implementation for [the racer library](https://github.com/phildawes/racer)
use engine::{SemanticEngine, Definition, Context, CursorPosition};

pub struct Racer;

use super::Result;

impl SemanticEngine for Racer {
    fn find_definition(&self, ctx: &Context) -> Result<Option<Definition>> {
        let path = ctx.query_path();

        let session = ::racer::core::Session::from_path(path, path);

        for buffer in ctx.buffers {
            session.cache_file_contents(buffer.path(), &buffer.contents[..]);
        }

        let query_src = &session.load_file(path).src.code[..];

        let pos = ::racer::scopes::coords_to_point(query_src,
                                                   ctx.query_cursor.line,
                                                   ctx.query_cursor.col);

        // TODO catch_panic: apparently this can panic! in a string operation. Something about pos
        // not landing on a character boundary.
        Ok(match ::racer::core::find_definition(query_src, path, pos, &session) {
            Some(m) => {
                // TODO modify racer Match to return line, col. For now, read the file it found a
                // match in to translate the Match position into line, col.
                let (line, col) = {
                    let found_src = &session.load_file(m.filepath.as_path());
                    let found_src_str = &found_src.src.code[..];
                    // TODO This can panic if the position is out of bounds :(
                    ::racer::scopes::point_to_coords(found_src_str, m.point)
                };

                let match_type = format!("{:?}", m.mtype);
                Some(Definition {
                    position: CursorPosition {
                        line: line,
                        col: col
                    },
                    dtype: match_type,
                    file_path: m.filepath.to_str().unwrap().to_string(),
                    text: m.matchstr.clone(),
                    text_context: m.contextstr.clone(),
                })
            },
            None => None
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use engine::{Context, CursorPosition, SemanticEngine, Buffer};

    use ::util::fs::TmpFile;

    #[test]
    #[allow(unused_variables)]
    fn find_definition() {
        // FIXME this is just here for side effects.
        let src2 = TmpFile::with_name("src2.rs", "
            pub fn myfn() {}
            pub fn foo() {}
            ");

        let src = "
            use src2::*;
            mod src2;
            fn main() {
                myfn();
            }";

        let buffers = vec![Buffer {
            contents: src.to_string(),
            file_path: "src.rs".to_string()
        }];

        let ctx = Context::new(&buffers, CursorPosition { line: 5, col: 17 }, "src.rs");
        let racer = Racer;
        let def = racer.find_definition(&ctx).unwrap().unwrap();

        assert_eq!(def.text, "myfn");
    }
}
<|endoftext|>