<commit_before>use analysis::bounds::Bound;
use analysis::imports::Imports;
use analysis::ref_mode::RefMode;
use analysis::rust_type::*;
use analysis::signatures::{Signature, Signatures};
use analysis::signals;
use analysis::trampolines;
use config;
use config::gobjects::GObject;
use env::Env;
use library;
use nameutil;
use traits::*;
use version::Version;

#[derive(Debug)]
pub struct Property {
    pub name: String,
    pub var_name: String,
    pub typ: library::TypeId,
    pub is_get: bool,
    pub func_name: String,
    pub nullable: library::Nullable,
    pub get_out_ref_mode: RefMode,
    pub set_in_ref_mode: RefMode,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub bound: Option<Bound>,
}

pub fn analyze(
    env: &Env,
    props: &[library::Property],
    type_tid: library::TypeId,
    generate_trait: bool,
    trampolines: &mut trampolines::Trampolines,
    obj: &GObject,
    imports: &mut Imports,
    signatures: &Signatures,
    deps: &[library::TypeId],
) -> (Vec<Property>, Vec<signals::Info>) {
    let mut properties = Vec::new();
    let mut notify_signals = Vec::new();

    for prop in props {
        let configured_properties = obj.properties.matched(&prop.name);
        if configured_properties.iter().any(|f| f.ignore) {
            continue;
        }

        if env.is_totally_deprecated(prop.deprecated_version) {
            continue;
        }

        let (getter, setter, notify_signal) = analyze_property(
            env,
            prop,
            type_tid,
            &configured_properties,
            generate_trait,
            trampolines,
            obj,
            imports,
            signatures,
            deps,
        );

        if let Some(notify_signal) = notify_signal {
            notify_signals.push(notify_signal);
        }

        if getter.is_none() && setter.is_none() {
            continue;
        }

        let type_string = rust_type(env, prop.typ);
        let used_type_string = used_rust_type(env, prop.typ);
        if let Some(prop) = getter {
            if let Ok(ref s) = used_type_string {
                imports.add_used_type(s, prop.version);
            }
            if type_string.is_ok() {
                imports.add("gobject_ffi", prop.version);
                imports.add("glib::Value", prop.version);
                imports.add("glib::StaticType", prop.version);
            }

            properties.push(prop);
        }
        if let Some(prop) = setter {
            if let Ok(ref s) = used_type_string {
                imports.add_used_type(s, prop.version);
            }
            if type_string.is_ok() {
                imports.add("gobject_ffi", prop.version);
                imports.add("glib::Value", prop.version);
            }

            if prop.bound.is_some() {
                imports.add("glib", prop.version);
                imports.add("glib::object::IsA", prop.version);
            }

            properties.push(prop);
        }
    }

    (properties, notify_signals)
}

fn analyze_property(
    env: &Env,
    prop: &library::Property,
    type_tid: library::TypeId,
    configured_properties: &[&config::properties::Property],
    generate_trait: bool,
    trampolines: &mut trampolines::Trampolines,
    obj: &GObject,
    imports: &mut Imports,
    signatures: &Signatures,
    deps: &[library::TypeId],
) -> (Option<Property>, Option<Property>, Option<signals::Info>) {
    let name = prop.name.clone();

    let prop_version = configured_properties
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(prop.version);
    let name_for_func = nameutil::signal_to_snake(&name);
    let var_name = nameutil::mangle_keywords(&*name_for_func).into_owned();
    let get_func_name = format!("get_property_{}", name_for_func);
    let set_func_name = format!("set_property_{}", name_for_func);
    let check_get_func_name = format!("get_{}", name_for_func);
    let check_set_func_name = format!("set_{}", name_for_func);

    let mut readable = prop.readable;
    let mut writable = if prop.construct_only {
        false
    } else {
        prop.writable
    };

    if readable {
        let (has, version) = Signature::has_for_property(env, &check_get_func_name,
                                                         true, prop.typ, signatures, deps);
        if has && (env.is_totally_deprecated(version) || version <= prop_version) {
            readable = false;
        }
    }
    if writable {
        let (has, version) = Signature::has_for_property(env, &check_set_func_name,
                                                         false, prop.typ, signatures, deps);
        if has && (env.is_totally_deprecated(version) || version <= prop_version) {
            writable = false;
        }
    }

    let get_out_ref_mode = RefMode::of(env, prop.typ, library::ParameterDirection::Return);
    let mut set_in_ref_mode = RefMode::of(env, prop.typ, library::ParameterDirection::In);
    if set_in_ref_mode == RefMode::ByRefMut {
        set_in_ref_mode = RefMode::ByRef;
    }
    let nullable = library::Nullable(set_in_ref_mode.is_ref());
    let getter = if readable {
        Some(Property {
            name: name.clone(),
            var_name: String::new(),
            typ: prop.typ,
            is_get: true,
            func_name: get_func_name,
            nullable: nullable,
            get_out_ref_mode: get_out_ref_mode,
            set_in_ref_mode: set_in_ref_mode,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
            bound: None,
        })
    } else {
        None
    };

    let setter = if writable {
        let bound = Bound::get_for_property_setter(env, &var_name, prop.typ, nullable);
        Some(Property {
            name: name.clone(),
            var_name: var_name,
            typ: prop.typ,
            is_get: false,
            func_name: set_func_name,
            nullable: nullable,
            get_out_ref_mode: get_out_ref_mode,
            set_in_ref_mode: set_in_ref_mode,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
            bound: bound,
        })
    } else {
        None
    };

    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let trampoline_name = trampolines::analyze(
        env,
        &library::Signal {
            name: format!("notify::{}", name),
            parameters: Vec::new(),
            ret: library::Parameter {
                name: "".into(),
                typ: env.library
                    .find_type(library::INTERNAL_NAMESPACE, "none")
                    .unwrap(),
                c_type: "none".into(),
                instance_parameter: false,
                direction: library::ParameterDirection::Return,
                transfer: library::Transfer::None,
                caller_allocates: false,
                nullable: library::Nullable(false),
                allow_none: false,
                array_length: None,
                is_error: false,
                doc: None,
                async: false,
            },
            is_action: false,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
            doc: None,
            doc_deprecated: None,
        },
        type_tid,
        generate_trait,
        &[],
        trampolines,
        obj,
        &mut used_types,
        prop_version,
    );

    let notify_signal = if trampoline_name.is_ok() {
        imports.add_used_types(&used_types, prop_version);
        if generate_trait {
            imports.add("glib", prop_version);
            imports.add("glib::object::Downcast", prop_version);
        }
        imports.add("glib::signal::connect", prop_version);
        imports.add("glib::signal::SignalHandlerId", prop_version);
        imports.add("std::mem::transmute", prop_version);
        imports.add("std::boxed::Box as Box_", prop_version);
        imports.add("glib_ffi", prop_version);

        Some(signals::Info {
            connect_name: format!("connect_property_{}_notify", name_for_func),
            signal_name: format!("notify::{}", name),
            trampoline_name: trampoline_name,
            action_emit_name: None,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
            doc_hidden: false,
        })
    } else {
        None
    };

    (getter, setter, notify_signal)
}
<commit_msg>Set property minimium version to better ignoring<commit_after>use analysis::bounds::Bound;
use analysis::imports::Imports;
use analysis::ref_mode::RefMode;
use analysis::rust_type::*;
use analysis::signatures::{Signature, Signatures};
use analysis::signals;
use analysis::trampolines;
use config;
use config::gobjects::GObject;
use env::Env;
use library;
use nameutil;
use traits::*;
use version::Version;

#[derive(Debug)]
pub struct Property {
    pub name: String,
    pub var_name: String,
    pub typ: library::TypeId,
    pub is_get: bool,
    pub func_name: String,
    pub nullable: library::Nullable,
    pub get_out_ref_mode: RefMode,
    pub set_in_ref_mode: RefMode,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub bound: Option<Bound>,
}

pub fn analyze(
    env: &Env,
    props: &[library::Property],
    type_tid: library::TypeId,
    generate_trait: bool,
    trampolines: &mut trampolines::Trampolines,
    obj: &GObject,
    imports: &mut Imports,
    signatures: &Signatures,
    deps: &[library::TypeId],
) -> (Vec<Property>, Vec<signals::Info>) {
    let mut properties = Vec::new();
    let mut notify_signals = Vec::new();

    for prop in props {
        let configured_properties = obj.properties.matched(&prop.name);
        if configured_properties.iter().any(|f| f.ignore) {
            continue;
        }

        if env.is_totally_deprecated(prop.deprecated_version) {
            continue;
        }

        let (getter, setter, notify_signal) = analyze_property(
            env,
            prop,
            type_tid,
            &configured_properties,
            generate_trait,
            trampolines,
            obj,
            imports,
            signatures,
            deps,
        );

        if let Some(notify_signal) = notify_signal {
            notify_signals.push(notify_signal);
        }

        if getter.is_none() && setter.is_none() {
            continue;
        }

        let type_string = rust_type(env, prop.typ);
        let used_type_string = used_rust_type(env, prop.typ);
        if let Some(prop) = getter {
            if let Ok(ref s) = used_type_string {
                imports.add_used_type(s, prop.version);
            }
            if type_string.is_ok() {
                imports.add("gobject_ffi", prop.version);
                imports.add("glib::Value", prop.version);
                imports.add("glib::StaticType", prop.version);
            }

            properties.push(prop);
        }
        if let Some(prop) = setter {
            if let Ok(ref s) = used_type_string {
                imports.add_used_type(s, prop.version);
            }
            if type_string.is_ok() {
                imports.add("gobject_ffi", prop.version);
                imports.add("glib::Value", prop.version);
            }

            if prop.bound.is_some() {
                imports.add("glib", prop.version);
                imports.add("glib::object::IsA", prop.version);
            }

            properties.push(prop);
        }
    }

    (properties, notify_signals)
}

fn analyze_property(
    env: &Env,
    prop: &library::Property,
    type_tid: library::TypeId,
    configured_properties: &[&config::properties::Property],
    generate_trait: bool,
    trampolines: &mut trampolines::Trampolines,
    obj: &GObject,
    imports: &mut Imports,
    signatures: &Signatures,
    deps: &[library::TypeId],
) -> (Option<Property>, Option<Property>, Option<signals::Info>) {
    let name = prop.name.clone();

    let prop_version = configured_properties
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(prop.version)
        .or(Some(env.config.min_cfg_version));
    let name_for_func = nameutil::signal_to_snake(&name);
    let var_name = nameutil::mangle_keywords(&*name_for_func).into_owned();
    let get_func_name = format!("get_property_{}", name_for_func);
    let set_func_name = format!("set_property_{}", name_for_func);
    let check_get_func_name = format!("get_{}", name_for_func);
    let check_set_func_name = format!("set_{}", name_for_func);

    let mut readable = prop.readable;
    let mut writable = if prop.construct_only {
        false
    } else {
        prop.writable
    };

    if readable {
        let (has, version) = Signature::has_for_property(env, &check_get_func_name,
                                                         true, prop.typ, signatures, deps);
        if has && (env.is_totally_deprecated(version) || version <= prop_version) {
            readable = false;
        }
    }
    if writable {
        let (has, version) = Signature::has_for_property(env, &check_set_func_name,
                                                         false, prop.typ, signatures, deps);
        if has && (env.is_totally_deprecated(version) || version <= prop_version) {
            writable = false;
        }
    }

    let get_out_ref_mode = RefMode::of(env, prop.typ, library::ParameterDirection::Return);
    let mut set_in_ref_mode = RefMode::of(env, prop.typ, library::ParameterDirection::In);
    if set_in_ref_mode == RefMode::ByRefMut {
        set_in_ref_mode = RefMode::ByRef;
    }
    let nullable = library::Nullable(set_in_ref_mode.is_ref());
    let getter = if readable {
        Some(Property {
            name: name.clone(),
            var_name: String::new(),
            typ: prop.typ,
            is_get: true,
            func_name: get_func_name,
            nullable: nullable,
            get_out_ref_mode: get_out_ref_mode,
            set_in_ref_mode: set_in_ref_mode,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
            bound: None,
        })
    } else {
        None
    };

    let setter = if writable {
        let bound = Bound::get_for_property_setter(env, &var_name, prop.typ, nullable);
        Some(Property {
            name: name.clone(),
            var_name: var_name,
            typ: prop.typ,
            is_get: false,
            func_name: set_func_name,
            nullable: nullable,
            get_out_ref_mode: get_out_ref_mode,
            set_in_ref_mode: set_in_ref_mode,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
            bound: bound,
        })
    } else {
        None
    };

    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let trampoline_name = trampolines::analyze(
        env,
        &library::Signal {
            name: format!("notify::{}", name),
            parameters: Vec::new(),
            ret: library::Parameter {
                name: "".into(),
                typ: env.library
                    .find_type(library::INTERNAL_NAMESPACE, "none")
                    .unwrap(),
                c_type: "none".into(),
                instance_parameter: false,
                direction: library::ParameterDirection::Return,
                transfer: library::Transfer::None,
                caller_allocates: false,
                nullable: library::Nullable(false),
                allow_none: false,
                array_length: None,
                is_error: false,
                doc: None,
                async: false,
            },
            is_action: false,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
            doc: None,
            doc_deprecated: None,
        },
        type_tid,
        generate_trait,
        &[],
        trampolines,
        obj,
        &mut used_types,
        prop_version,
    );

    let notify_signal = if trampoline_name.is_ok() {
        imports.add_used_types(&used_types, prop_version);
        if generate_trait {
            imports.add("glib", prop_version);
            imports.add("glib::object::Downcast", prop_version);
        }
        imports.add("glib::signal::connect", prop_version);
        imports.add("glib::signal::SignalHandlerId", prop_version);
        imports.add("std::mem::transmute", prop_version);
        imports.add("std::boxed::Box as Box_", prop_version);
        imports.add("glib_ffi", prop_version);

        Some(signals::Info {
            connect_name: format!("connect_property_{}_notify", name_for_func),
            signal_name: format!("notify::{}", name),
            trampoline_name: trampoline_name,
            action_emit_name: None,
            version: prop_version,
            deprecated_version: prop.deprecated_version,
            doc_hidden: false,
        })
    } else {
        None
    };

    (getter, setter, notify_signal)
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#[crate_id = "github.com/mozilla/servo"];
#[comment = "The Servo Parallel Browser Project"];
#[license = "MPL"];

#[feature(globs, macro_rules, managed_boxes, phase, thread_local)];

#[feature(phase)];
#[phase(syntax, link)]
extern crate log;

extern crate alert;
extern crate azure;
extern crate geom;
extern crate gfx;
#[cfg(not(target_os="android"))]
extern crate glfw = "glfw-rs";
#[cfg(target_os="android")]
extern crate glut;
extern crate js;
extern crate layers;
extern crate opengles;
extern crate png;
extern crate rustuv;
extern crate script;
#[phase(syntax)]
extern crate servo_macros = "macros";
extern crate servo_net = "net";
extern crate servo_msg = "msg";
extern crate servo_util = "util";
extern crate style;
extern crate sharegl;
extern crate stb_image;

extern crate collections;
extern crate green;
extern crate native;
extern crate serialize;
extern crate sync;
extern crate time;
extern crate url;

#[cfg(target_os="macos")]
extern crate core_graphics;
#[cfg(target_os="macos")]
extern crate core_text;

#[cfg(not(test))]
use compositing::{CompositorChan, CompositorTask};
#[cfg(not(test))]
use constellation::Constellation;
#[cfg(not(test))]
use servo_msg::constellation_msg::{ConstellationChan, InitLoadUrlMsg};

#[cfg(not(test))]
use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask};
#[cfg(not(test))]
use servo_net::resource_task::ResourceTask;
#[cfg(not(test))]
use servo_util::time::Profiler;

#[cfg(not(test))]
use servo_util::opts;
#[cfg(not(test))]
use servo_util::url::parse_url;

#[cfg(not(test), not(target_os="android"))]
use std::os;
#[cfg(not(test), target_os="android")]
use std::str;
#[cfg(not(test))]
use std::task::TaskOpts;
#[cfg(not(test))]
use url::Url;

#[path="compositing/compositor_task.rs"]
pub mod compositing;

pub mod css {
    mod node_util;

    pub mod select;
    pub mod matching;
    pub mod node_style;
}

pub mod constellation;
pub mod pipeline;

pub mod layout {
    pub mod block;
    pub mod box_;
    pub mod construct;
    pub mod context;
    pub mod display_list_builder;
    pub mod floats;
    pub mod flow;
    pub mod flow_list;
    pub mod layout_task;
    pub mod inline;
    pub mod model;
    pub mod parallel;
    pub mod table_wrapper;
    pub mod table;
    pub mod table_caption;
    pub mod table_colgroup;
    pub mod table_rowgroup;
    pub mod table_row;
    pub mod table_cell;
    pub mod text;
    pub mod util;
    pub mod incremental;
    pub mod wrapper;
    pub mod extra;
}

pub mod windowing;

#[path="platform/mod.rs"]
pub mod platform;

#[cfg(not(test), target_os="linux")]
#[cfg(not(test), target_os="macos")]
#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, proc() {
        opts::from_cmdline_args(os::args()).map(run);
    })
}

#[cfg(not(test), target_os="android")]
#[no_mangle]
pub extern "C" fn android_start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, proc() {
        let mut args:~[~str] = ~[];
        for i in range(0u, argc as uint) {
            unsafe {
                args.push(str::raw::from_c_str(*argv.offset(i as int) as *i8));
            }
        }
        opts::from_cmdline_args(args).map(run);
    })
}

#[cfg(not(test))]
fn run(opts: opts::Opts) {
    let mut pool = green::SchedPool::new(green::PoolConfig::new());

    let (compositor_port, compositor_chan) = CompositorChan::new();
    let profiler_chan = Profiler::create(opts.profiler_period);

    let opts_clone = opts.clone();
    let profiler_chan_clone = profiler_chan.clone();

    let (result_chan, result_port) = channel();
    pool.spawn(TaskOpts::new(), proc() {
        let opts = &opts_clone;
        // Create a Servo instance.
        let resource_task = ResourceTask();
        // If we are emitting an output file, then we need to block on
        // image load or we risk emitting an output file missing the
        // image.
        let image_cache_task = if opts.output_file.is_some() {
                SyncImageCacheTask(resource_task.clone())
            } else {
                ImageCacheTask(resource_task.clone())
            };
        let constellation_chan = Constellation::start(compositor_chan,
                                                      opts,
                                                      resource_task,
                                                      image_cache_task,
                                                      profiler_chan_clone);

        // Send the URL command to the constellation.
        for filename in opts.urls.iter() {
            let url = if filename.starts_with("data:") {
                // As a hack for easier command-line testing,
                // assume that data URLs are not URL-encoded.
                Url::new(~"data", None, ~"", None,
                    filename.slice_from(5).to_owned(), Vec::new(), None)
            } else {
                parse_url(*filename, None)
            };

            let ConstellationChan(ref chan) = constellation_chan;
            chan.send(InitLoadUrlMsg(url));
        }

        // Send the constallation Chan as the result
        result_chan.send(constellation_chan);
    });

    let constellation_chan = result_port.recv();

    debug!("preparing to enter main loop");
    CompositorTask::create(opts,
                           compositor_port,
                           constellation_chan,
                           profiler_chan);

    pool.shutdown();
}

<commit_msg>Default to CPU renering on Android to avoid GPU-rendering-specific driver woes<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#[crate_id = "github.com/mozilla/servo"];
#[comment = "The Servo Parallel Browser Project"];
#[license = "MPL"];

#[feature(globs, macro_rules, managed_boxes, phase, thread_local)];

#[feature(phase)];
#[phase(syntax, link)]
extern crate log;

extern crate alert;
extern crate azure;
extern crate geom;
extern crate gfx;
#[cfg(not(target_os="android"))]
extern crate glfw = "glfw-rs";
#[cfg(target_os="android")]
extern crate glut;
extern crate js;
extern crate layers;
extern crate opengles;
extern crate png;
extern crate rustuv;
extern crate script;
#[phase(syntax)]
extern crate servo_macros = "macros";
extern crate servo_net = "net";
extern crate servo_msg = "msg";
extern crate servo_util = "util";
extern crate style;
extern crate sharegl;
extern crate stb_image;

extern crate collections;
extern crate green;
extern crate native;
extern crate serialize;
extern crate sync;
extern crate time;
extern crate url;

#[cfg(target_os="macos")]
extern crate core_graphics;
#[cfg(target_os="macos")]
extern crate core_text;

#[cfg(not(test))]
use compositing::{CompositorChan, CompositorTask};
#[cfg(not(test))]
use constellation::Constellation;
#[cfg(not(test))]
use servo_msg::constellation_msg::{ConstellationChan, InitLoadUrlMsg};

#[cfg(not(test))]
use servo_net::image_cache_task::{ImageCacheTask, SyncImageCacheTask};
#[cfg(not(test))]
use servo_net::resource_task::ResourceTask;
#[cfg(not(test))]
use servo_util::time::Profiler;

#[cfg(not(test))]
use servo_util::opts;
#[cfg(not(test))]
use servo_util::url::parse_url;

#[cfg(not(test), not(target_os="android"))]
use std::os;
#[cfg(not(test), target_os="android")]
use std::str;
#[cfg(not(test))]
use std::task::TaskOpts;
#[cfg(not(test))]
use url::Url;

#[path="compositing/compositor_task.rs"]
pub mod compositing;

pub mod css {
    mod node_util;

    pub mod select;
    pub mod matching;
    pub mod node_style;
}

pub mod constellation;
pub mod pipeline;

pub mod layout {
    pub mod block;
    pub mod box_;
    pub mod construct;
    pub mod context;
    pub mod display_list_builder;
    pub mod floats;
    pub mod flow;
    pub mod flow_list;
    pub mod layout_task;
    pub mod inline;
    pub mod model;
    pub mod parallel;
    pub mod table_wrapper;
    pub mod table;
    pub mod table_caption;
    pub mod table_colgroup;
    pub mod table_rowgroup;
    pub mod table_row;
    pub mod table_cell;
    pub mod text;
    pub mod util;
    pub mod incremental;
    pub mod wrapper;
    pub mod extra;
}

pub mod windowing;

#[path="platform/mod.rs"]
pub mod platform;

#[cfg(not(test), target_os="linux")]
#[cfg(not(test), target_os="macos")]
#[start]
fn start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, proc() {
        opts::from_cmdline_args(os::args()).map(run);
    })
}

#[cfg(not(test), target_os="android")]
#[no_mangle]
pub extern "C" fn android_start(argc: int, argv: **u8) -> int {
    native::start(argc, argv, proc() {
        let mut args:~[~str] = ~[];
        for i in range(0u, argc as uint) {
            unsafe {
                args.push(str::raw::from_c_str(*argv.offset(i as int) as *i8));
            }
        }

        let mut opts = opts::from_cmdline_args(args);
        match opts {
            Some(mut o) => {
                // Always use CPU rendering on android.
                o.cpu_painting = true;
                run(o);
            },
            None => {}
        }
    })
}

#[cfg(not(test))]
fn run(opts: opts::Opts) {
    let mut pool = green::SchedPool::new(green::PoolConfig::new());

    let (compositor_port, compositor_chan) = CompositorChan::new();
    let profiler_chan = Profiler::create(opts.profiler_period);

    let opts_clone = opts.clone();
    let profiler_chan_clone = profiler_chan.clone();

    let (result_chan, result_port) = channel();
    pool.spawn(TaskOpts::new(), proc() {
        let opts = &opts_clone;
        // Create a Servo instance.
        let resource_task = ResourceTask();
        // If we are emitting an output file, then we need to block on
        // image load or we risk emitting an output file missing the
        // image.
        let image_cache_task = if opts.output_file.is_some() {
                SyncImageCacheTask(resource_task.clone())
            } else {
                ImageCacheTask(resource_task.clone())
            };
        let constellation_chan = Constellation::start(compositor_chan,
                                                      opts,
                                                      resource_task,
                                                      image_cache_task,
                                                      profiler_chan_clone);

        // Send the URL command to the constellation.
        for filename in opts.urls.iter() {
            let url = if filename.starts_with("data:") {
                // As a hack for easier command-line testing,
                // assume that data URLs are not URL-encoded.
                Url::new(~"data", None, ~"", None,
                    filename.slice_from(5).to_owned(), Vec::new(), None)
            } else {
                parse_url(*filename, None)
            };

            let ConstellationChan(ref chan) = constellation_chan;
            chan.send(InitLoadUrlMsg(url));
        }

        // Send the constallation Chan as the result
        result_chan.send(constellation_chan);
    });

    let constellation_chan = result_port.recv();

    debug!("preparing to enter main loop");
    CompositorTask::create(opts,
                           compositor_port,
                           constellation_chan,
                           profiler_chan);

    pool.shutdown();
}

<|endoftext|>


<commit_before>//! Conversions to different `Term` encodings

use term::Term;

/// Conversion to Church encoding
pub trait IntoChurch {
    /// Convert an object into a Church-encoded `Term`
    fn into_church(self) -> Term;
}

/// Conversion to Scott encoding
pub trait IntoScott {
    /// Convert an object into a Scott-encoded `Term`
    fn into_scott(self) -> Term;
}

/// Conversion to Parigot encoding
pub trait IntoParigot {
    /// Convert an object into a Parigot-encoded `Term`
    fn into_parigot(self) -> Term;
}

/// Conversion to Stump-Fu encoding
pub trait IntoStumpFu {
    /// Convert an object into a Stump-Fu-encoded `Term`
    fn into_stumpfu(self) -> Term;
}
<commit_msg>generate conversion traits with a macro<commit_after>//! Conversions to different `Term` encodings

#![allow(missing_docs)]

use term::Term;

macro_rules! make_trait {
    ($trait_name:ident, $function_name:ident) => (
        pub trait $trait_name {
            #[doc="Performs the conversion."]
            fn $function_name(self) -> Term;
        }
    );
}

make_trait!(IntoChurch, into_church);
make_trait!(IntoScott, into_scott);
make_trait!(IntoParigot, into_parigot);
make_trait!(IntoStumpFu, into_stumpfu);
<|endoftext|>


<commit_before>//! Name resolution algorithm
use std::{
    sync::Arc,
    time::Instant,
};

use rustc_hash::FxHashMap;

use ra_syntax::{
    SmolStr, SyntaxKind::{self, *},
    ast::{self, AstNode, ModuleItemOwner}
};

use crate::{
    Cancelable,
    loc2id::{DefId, DefLoc},
    descriptors::{
        DescriptorDatabase,
        module::{ModuleId, ModuleTree, ModuleSourceNode},
    },
    syntax_ptr::{LocalSyntaxPtr},
    input::SourceRootId,
};

/// Item map is the result of the name resolution. Item map contains, for each
/// module, the set of visible items.
#[derive(Default, Debug, PartialEq, Eq)]
pub(crate) struct ItemMap {
    pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub(crate) struct ModuleScope {
    pub(crate) items: FxHashMap<SmolStr, Resolution>,
    pub(crate) import_resolutions: FxHashMap<LocalSyntaxPtr, DefId>,
}

/// A set of items and imports declared inside a module, without relation to
/// other modules.
///
/// This stands in-between raw syntax and name resolution and alow us to avoid
/// recomputing name res: if `InputModuleItems` are the same, we can avoid
/// running name resolution.
#[derive(Debug, Default, PartialEq, Eq)]
pub(crate) struct InputModuleItems {
    items: Vec<ModuleItem>,
    glob_imports: Vec<Path>,
    imports: Vec<Path>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct Path {
    kind: PathKind,
    segments: Vec<(LocalSyntaxPtr, SmolStr)>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PathKind {
    Abs,
    Self_,
    Super,
    Crate,
}

pub(crate) fn input_module_items(
    db: &impl DescriptorDatabase,
    source_root: SourceRootId,
    module_id: ModuleId,
) -> Cancelable<Arc<InputModuleItems>> {
    let module_tree = db._module_tree(source_root)?;
    let source = module_id.source(&module_tree);
    let res = match source.resolve(db) {
        ModuleSourceNode::SourceFile(it) => {
            let items = it.borrowed().items();
            InputModuleItems::new(items)
        }
        ModuleSourceNode::Module(it) => {
            let items = it
                .borrowed()
                .item_list()
                .into_iter()
                .flat_map(|it| it.items());
            InputModuleItems::new(items)
        }
    };
    Ok(Arc::new(res))
}

pub(crate) fn item_map(
    db: &impl DescriptorDatabase,
    source_root: SourceRootId,
) -> Cancelable<Arc<ItemMap>> {
    let start = Instant::now();
    let module_tree = db._module_tree(source_root)?;
    let input = module_tree
        .modules()
        .map(|id| {
            let items = db._input_module_items(source_root, id)?;
            Ok((id, items))
        })
        .collect::<Cancelable<FxHashMap<_, _>>>()?;

    let mut resolver = Resolver {
        db: db,
        input: &input,
        source_root,
        module_tree,
        result: ItemMap::default(),
    };
    resolver.resolve()?;
    let res = resolver.result;
    let elapsed = start.elapsed();
    log::info!("item_map: {:?}", elapsed);
    Ok(Arc::new(res))
}

/// Resolution is basically `DefId` atm, but it should account for stuff like
/// multiple namespaces, ambiguity and errors.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Resolution {
    /// None for unresolved
    pub(crate) def_id: Option<DefId>,
    /// ident by whitch this is imported into local scope.
    /// TODO: make this offset-independent.
    pub(crate) import_name: Option<LocalSyntaxPtr>,
}

// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
// enum Namespace {
//     Types,
//     Values,
// }

// #[derive(Debug)]
// struct PerNs<T> {
//     types: Option<T>,
//     values: Option<T>,
// }

#[derive(Debug, PartialEq, Eq)]
struct ModuleItem {
    ptr: LocalSyntaxPtr,
    name: SmolStr,
    kind: SyntaxKind,
    vis: Vis,
}

#[derive(Debug, PartialEq, Eq)]
enum Vis {
    // Priv,
    Other,
}

impl InputModuleItems {
    fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems {
        let mut res = InputModuleItems::default();
        for item in items {
            res.add_item(item);
        }
        res
    }

    fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> {
        match item {
            ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::ImplItem(_) => {
                // impls don't define items
            }
            ast::ModuleItem::UseItem(it) => self.add_use_item(it),
            ast::ModuleItem::ExternCrateItem(_) => {
                // TODO
            }
            ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?),
        }
        Some(())
    }

    fn add_use_item(&mut self, item: ast::UseItem) {
        if let Some(tree) = item.use_tree() {
            self.add_use_tree(None, tree);
        }
    }

    fn add_use_tree(&mut self, prefix: Option<Path>, tree: ast::UseTree) {
        if let Some(use_tree_list) = tree.use_tree_list() {
            let prefix = match tree.path() {
                None => prefix,
                Some(path) => match convert_path(prefix, path) {
                    Some(it) => Some(it),
                    None => return, // TODO: report errors somewhere
                },
            };
            for tree in use_tree_list.use_trees() {
                self.add_use_tree(prefix.clone(), tree);
            }
        } else {
            if let Some(path) = tree.path() {
                if let Some(path) = convert_path(prefix, path) {
                    if tree.has_star() {
                        &mut self.glob_imports
                    } else {
                        &mut self.imports
                    }
                    .push(path);
                }
            }
        }
    }
}

fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
    let prefix = if let Some(qual) = path.qualifier() {
        Some(convert_path(prefix, qual)?)
    } else {
        None
    };
    let segment = path.segment()?;
    let res = match segment.kind()? {
        ast::PathSegmentKind::Name(name) => {
            let mut res = prefix.unwrap_or_else(|| Path {
                kind: PathKind::Abs,
                segments: Vec::with_capacity(1),
            });
            let ptr = LocalSyntaxPtr::new(name.syntax());
            res.segments.push((ptr, name.text()));
            res
        }
        ast::PathSegmentKind::CrateKw => {
            if prefix.is_some() {
                return None;
            }
            Path {
                kind: PathKind::Crate,
                segments: Vec::new(),
            }
        }
        ast::PathSegmentKind::SelfKw => {
            if prefix.is_some() {
                return None;
            }
            Path {
                kind: PathKind::Self_,
                segments: Vec::new(),
            }
        }
        ast::PathSegmentKind::SuperKw => {
            if prefix.is_some() {
                return None;
            }
            Path {
                kind: PathKind::Super,
                segments: Vec::new(),
            }
        }
    };
    Some(res)
}

impl ModuleItem {
    fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
        let name = item.name()?.text();
        let ptr = LocalSyntaxPtr::new(item.syntax());
        let kind = item.syntax().kind();
        let vis = Vis::Other;
        let res = ModuleItem {
            ptr,
            name,
            kind,
            vis,
        };
        Some(res)
    }
}

struct Resolver<'a, DB> {
    db: &'a DB,
    input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
    source_root: SourceRootId,
    module_tree: Arc<ModuleTree>,
    result: ItemMap,
}

impl<'a, DB> Resolver<'a, DB>
where
    DB: DescriptorDatabase,
{
    fn resolve(&mut self) -> Cancelable<()> {
        for (&module_id, items) in self.input.iter() {
            self.populate_module(module_id, items)
        }

        for &module_id in self.input.keys() {
            crate::db::check_canceled(self.db)?;
            self.resolve_imports(module_id);
        }
        Ok(())
    }

    fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
        let file_id = module_id.source(&self.module_tree).file_id();

        let mut module_items = ModuleScope::default();

        for import in input.imports.iter() {
            if let Some((ptr, name)) = import.segments.last() {
                module_items.items.insert(
                    name.clone(),
                    Resolution {
                        def_id: None,
                        import_name: Some(*ptr),
                    },
                );
            }
        }

        for item in input.items.iter() {
            if item.kind == MODULE {
                // handle submodules separatelly
                continue;
            }
            let ptr = item.ptr.into_global(file_id);
            let def_loc = DefLoc::Item { ptr };
            let def_id = self.db.id_maps().def_id(def_loc);
            let resolution = Resolution {
                def_id: Some(def_id),
                import_name: None,
            };
            module_items.items.insert(item.name.clone(), resolution);
        }

        for (name, mod_id) in module_id.children(&self.module_tree) {
            let def_loc = DefLoc::Module {
                id: mod_id,
                source_root: self.source_root,
            };
            let def_id = self.db.id_maps().def_id(def_loc);
            let resolution = Resolution {
                def_id: Some(def_id),
                import_name: None,
            };
            module_items.items.insert(name, resolution);
        }

        self.result.per_module.insert(module_id, module_items);
    }

    fn resolve_imports(&mut self, module_id: ModuleId) {
        for import in self.input[&module_id].imports.iter() {
            self.resolve_import(module_id, import);
        }
    }

    fn resolve_import(&mut self, module_id: ModuleId, import: &Path) {
        let mut curr = match import.kind {
            // TODO: handle extern crates
            PathKind::Abs => return,
            PathKind::Self_ => module_id,
            PathKind::Super => {
                match module_id.parent(&self.module_tree) {
                    Some(it) => it,
                    // TODO: error
                    None => return,
                }
            }
            PathKind::Crate => module_id.crate_root(&self.module_tree),
        };

        for (i, (ptr, name)) in import.segments.iter().enumerate() {
            let is_last = i == import.segments.len() - 1;

            let def_id = match self.result.per_module[&curr].items.get(name) {
                None => return,
                Some(res) => match res.def_id {
                    Some(it) => it,
                    None => return,
                },
            };

            self.update(module_id, |items| {
                items.import_resolutions.insert(*ptr, def_id);
            });

            if !is_last {
                curr = match self.db.id_maps().def_loc(def_id) {
                    DefLoc::Module { id, .. } => id,
                    _ => return,
                }
            } else {
                self.update(module_id, |items| {
                    let res = Resolution {
                        def_id: Some(def_id),
                        import_name: Some(*ptr),
                    };
                    items.items.insert(name.clone(), res);
                })
            }
        }
    }

    fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
        let module_items = self.result.per_module.get_mut(&module_id).unwrap();
        f(module_items)
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        mock_analysis::analysis_and_position,
        descriptors::{DescriptorDatabase, module::ModuleDescriptor},
        input::FilesDatabase,
};
    use super::*;

    fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
        let (analysis, pos) = analysis_and_position(fixture);
        let db = analysis.imp.db;
        let source_root = db.file_source_root(pos.file_id);
        let descr = ModuleDescriptor::guess_from_position(&*db, pos)
            .unwrap()
            .unwrap();
        let module_id = descr.module_id;
        (db._item_map(source_root).unwrap(), module_id)
    }

    #[test]
    fn test_item_map() {
        let (item_map, module_id) = item_map(
            "
            //- /lib.rs
            mod foo;

            use crate::foo::bar::Baz;
            <|>

            //- /foo/mod.rs
            pub mod bar;

            //- /foo/bar.rs
            pub struct Baz;
        ",
        );
        let name = SmolStr::from("Baz");
        let resolution = &item_map.per_module[&module_id].items[&name];
        assert!(resolution.def_id.is_some());
    }
}
<commit_msg>Remove import_resolutions<commit_after>//! Name resolution algorithm
use std::{
    sync::Arc,
    time::Instant,
};

use rustc_hash::FxHashMap;

use ra_syntax::{
    SmolStr, SyntaxKind::{self, *},
    ast::{self, AstNode, ModuleItemOwner}
};

use crate::{
    Cancelable,
    loc2id::{DefId, DefLoc},
    descriptors::{
        DescriptorDatabase,
        module::{ModuleId, ModuleTree, ModuleSourceNode},
    },
    syntax_ptr::{LocalSyntaxPtr},
    input::SourceRootId,
};

/// Item map is the result of the name resolution. Item map contains, for each
/// module, the set of visible items.
#[derive(Default, Debug, PartialEq, Eq)]
pub(crate) struct ItemMap {
    pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub(crate) struct ModuleScope {
    pub(crate) items: FxHashMap<SmolStr, Resolution>,
}

/// A set of items and imports declared inside a module, without relation to
/// other modules.
///
/// This stands in-between raw syntax and name resolution and alow us to avoid
/// recomputing name res: if `InputModuleItems` are the same, we can avoid
/// running name resolution.
#[derive(Debug, Default, PartialEq, Eq)]
pub(crate) struct InputModuleItems {
    items: Vec<ModuleItem>,
    glob_imports: Vec<Path>,
    imports: Vec<Path>,
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct Path {
    kind: PathKind,
    segments: Vec<(LocalSyntaxPtr, SmolStr)>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PathKind {
    Abs,
    Self_,
    Super,
    Crate,
}

pub(crate) fn input_module_items(
    db: &impl DescriptorDatabase,
    source_root: SourceRootId,
    module_id: ModuleId,
) -> Cancelable<Arc<InputModuleItems>> {
    let module_tree = db._module_tree(source_root)?;
    let source = module_id.source(&module_tree);
    let res = match source.resolve(db) {
        ModuleSourceNode::SourceFile(it) => {
            let items = it.borrowed().items();
            InputModuleItems::new(items)
        }
        ModuleSourceNode::Module(it) => {
            let items = it
                .borrowed()
                .item_list()
                .into_iter()
                .flat_map(|it| it.items());
            InputModuleItems::new(items)
        }
    };
    Ok(Arc::new(res))
}

pub(crate) fn item_map(
    db: &impl DescriptorDatabase,
    source_root: SourceRootId,
) -> Cancelable<Arc<ItemMap>> {
    let start = Instant::now();
    let module_tree = db._module_tree(source_root)?;
    let input = module_tree
        .modules()
        .map(|id| {
            let items = db._input_module_items(source_root, id)?;
            Ok((id, items))
        })
        .collect::<Cancelable<FxHashMap<_, _>>>()?;

    let mut resolver = Resolver {
        db: db,
        input: &input,
        source_root,
        module_tree,
        result: ItemMap::default(),
    };
    resolver.resolve()?;
    let res = resolver.result;
    let elapsed = start.elapsed();
    log::info!("item_map: {:?}", elapsed);
    Ok(Arc::new(res))
}

/// Resolution is basically `DefId` atm, but it should account for stuff like
/// multiple namespaces, ambiguity and errors.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct Resolution {
    /// None for unresolved
    pub(crate) def_id: Option<DefId>,
    /// ident by whitch this is imported into local scope.
    /// TODO: make this offset-independent.
    pub(crate) import_name: Option<LocalSyntaxPtr>,
}

// #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
// enum Namespace {
//     Types,
//     Values,
// }

// #[derive(Debug)]
// struct PerNs<T> {
//     types: Option<T>,
//     values: Option<T>,
// }

#[derive(Debug, PartialEq, Eq)]
struct ModuleItem {
    ptr: LocalSyntaxPtr,
    name: SmolStr,
    kind: SyntaxKind,
    vis: Vis,
}

#[derive(Debug, PartialEq, Eq)]
enum Vis {
    // Priv,
    Other,
}

impl InputModuleItems {
    fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> InputModuleItems {
        let mut res = InputModuleItems::default();
        for item in items {
            res.add_item(item);
        }
        res
    }

    fn add_item(&mut self, item: ast::ModuleItem) -> Option<()> {
        match item {
            ast::ModuleItem::StructDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::EnumDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::FnDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::TraitDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::TypeDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::ImplItem(_) => {
                // impls don't define items
            }
            ast::ModuleItem::UseItem(it) => self.add_use_item(it),
            ast::ModuleItem::ExternCrateItem(_) => {
                // TODO
            }
            ast::ModuleItem::ConstDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::StaticDef(it) => self.items.push(ModuleItem::new(it)?),
            ast::ModuleItem::Module(it) => self.items.push(ModuleItem::new(it)?),
        }
        Some(())
    }

    fn add_use_item(&mut self, item: ast::UseItem) {
        if let Some(tree) = item.use_tree() {
            self.add_use_tree(None, tree);
        }
    }

    fn add_use_tree(&mut self, prefix: Option<Path>, tree: ast::UseTree) {
        if let Some(use_tree_list) = tree.use_tree_list() {
            let prefix = match tree.path() {
                None => prefix,
                Some(path) => match convert_path(prefix, path) {
                    Some(it) => Some(it),
                    None => return, // TODO: report errors somewhere
                },
            };
            for tree in use_tree_list.use_trees() {
                self.add_use_tree(prefix.clone(), tree);
            }
        } else {
            if let Some(path) = tree.path() {
                if let Some(path) = convert_path(prefix, path) {
                    if tree.has_star() {
                        &mut self.glob_imports
                    } else {
                        &mut self.imports
                    }
                    .push(path);
                }
            }
        }
    }
}

fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
    let prefix = if let Some(qual) = path.qualifier() {
        Some(convert_path(prefix, qual)?)
    } else {
        None
    };
    let segment = path.segment()?;
    let res = match segment.kind()? {
        ast::PathSegmentKind::Name(name) => {
            let mut res = prefix.unwrap_or_else(|| Path {
                kind: PathKind::Abs,
                segments: Vec::with_capacity(1),
            });
            let ptr = LocalSyntaxPtr::new(name.syntax());
            res.segments.push((ptr, name.text()));
            res
        }
        ast::PathSegmentKind::CrateKw => {
            if prefix.is_some() {
                return None;
            }
            Path {
                kind: PathKind::Crate,
                segments: Vec::new(),
            }
        }
        ast::PathSegmentKind::SelfKw => {
            if prefix.is_some() {
                return None;
            }
            Path {
                kind: PathKind::Self_,
                segments: Vec::new(),
            }
        }
        ast::PathSegmentKind::SuperKw => {
            if prefix.is_some() {
                return None;
            }
            Path {
                kind: PathKind::Super,
                segments: Vec::new(),
            }
        }
    };
    Some(res)
}

impl ModuleItem {
    fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
        let name = item.name()?.text();
        let ptr = LocalSyntaxPtr::new(item.syntax());
        let kind = item.syntax().kind();
        let vis = Vis::Other;
        let res = ModuleItem {
            ptr,
            name,
            kind,
            vis,
        };
        Some(res)
    }
}

struct Resolver<'a, DB> {
    db: &'a DB,
    input: &'a FxHashMap<ModuleId, Arc<InputModuleItems>>,
    source_root: SourceRootId,
    module_tree: Arc<ModuleTree>,
    result: ItemMap,
}

impl<'a, DB> Resolver<'a, DB>
where
    DB: DescriptorDatabase,
{
    fn resolve(&mut self) -> Cancelable<()> {
        for (&module_id, items) in self.input.iter() {
            self.populate_module(module_id, items)
        }

        for &module_id in self.input.keys() {
            crate::db::check_canceled(self.db)?;
            self.resolve_imports(module_id);
        }
        Ok(())
    }

    fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
        let file_id = module_id.source(&self.module_tree).file_id();

        let mut module_items = ModuleScope::default();

        for import in input.imports.iter() {
            if let Some((ptr, name)) = import.segments.last() {
                module_items.items.insert(
                    name.clone(),
                    Resolution {
                        def_id: None,
                        import_name: Some(*ptr),
                    },
                );
            }
        }

        for item in input.items.iter() {
            if item.kind == MODULE {
                // handle submodules separatelly
                continue;
            }
            let ptr = item.ptr.into_global(file_id);
            let def_loc = DefLoc::Item { ptr };
            let def_id = self.db.id_maps().def_id(def_loc);
            let resolution = Resolution {
                def_id: Some(def_id),
                import_name: None,
            };
            module_items.items.insert(item.name.clone(), resolution);
        }

        for (name, mod_id) in module_id.children(&self.module_tree) {
            let def_loc = DefLoc::Module {
                id: mod_id,
                source_root: self.source_root,
            };
            let def_id = self.db.id_maps().def_id(def_loc);
            let resolution = Resolution {
                def_id: Some(def_id),
                import_name: None,
            };
            module_items.items.insert(name, resolution);
        }

        self.result.per_module.insert(module_id, module_items);
    }

    fn resolve_imports(&mut self, module_id: ModuleId) {
        for import in self.input[&module_id].imports.iter() {
            self.resolve_import(module_id, import);
        }
    }

    fn resolve_import(&mut self, module_id: ModuleId, import: &Path) {
        let mut curr = match import.kind {
            // TODO: handle extern crates
            PathKind::Abs => return,
            PathKind::Self_ => module_id,
            PathKind::Super => {
                match module_id.parent(&self.module_tree) {
                    Some(it) => it,
                    // TODO: error
                    None => return,
                }
            }
            PathKind::Crate => module_id.crate_root(&self.module_tree),
        };

        for (i, (ptr, name)) in import.segments.iter().enumerate() {
            let is_last = i == import.segments.len() - 1;

            let def_id = match self.result.per_module[&curr].items.get(name) {
                None => return,
                Some(res) => match res.def_id {
                    Some(it) => it,
                    None => return,
                },
            };

            if !is_last {
                curr = match self.db.id_maps().def_loc(def_id) {
                    DefLoc::Module { id, .. } => id,
                    _ => return,
                }
            } else {
                self.update(module_id, |items| {
                    let res = Resolution {
                        def_id: Some(def_id),
                        import_name: Some(*ptr),
                    };
                    items.items.insert(name.clone(), res);
                })
            }
        }
    }

    fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
        let module_items = self.result.per_module.get_mut(&module_id).unwrap();
        f(module_items)
    }
}

#[cfg(test)]
mod tests {
    use crate::{
        mock_analysis::analysis_and_position,
        descriptors::{DescriptorDatabase, module::ModuleDescriptor},
        input::FilesDatabase,
};
    use super::*;

    fn item_map(fixture: &str) -> (Arc<ItemMap>, ModuleId) {
        let (analysis, pos) = analysis_and_position(fixture);
        let db = analysis.imp.db;
        let source_root = db.file_source_root(pos.file_id);
        let descr = ModuleDescriptor::guess_from_position(&*db, pos)
            .unwrap()
            .unwrap();
        let module_id = descr.module_id;
        (db._item_map(source_root).unwrap(), module_id)
    }

    #[test]
    fn test_item_map() {
        let (item_map, module_id) = item_map(
            "
            //- /lib.rs
            mod foo;

            use crate::foo::bar::Baz;
            <|>

            //- /foo/mod.rs
            pub mod bar;

            //- /foo/bar.rs
            pub struct Baz;
        ",
        );
        let name = SmolStr::from("Baz");
        let resolution = &item_map.per_module[&module_id].items[&name];
        assert!(resolution.def_id.is_some());
    }
}
<|endoftext|>


<commit_before>use crate::AsArray2D;
use russell_lab::Matrix;
use std::collections::HashMap;

pub enum BlockKind {
    Qua4,
    Qua8,
    Hex8,
    Hex20,
}

pub enum Constraint {}

pub struct ConstraintData {
    kind: Constraint,
}

/// Defines a polygon on polyhedron that can be split into smaller shapes
///
/// # Block shape
///
/// ## 2D: Local indices of points and edges
///
/// ```text
///             Points (4 or 8)                        Edges (4)
///   y
///   |        3      6      2                             2
///   +--x      @-----@-----@                        +-----------+
///             |           |                        |           |
///             |           |                        |           |
///           7 @           @ 5                     3|           |1
///             |           |                        |           |
///             |           |                        |           |
///             @-----@-----@                        +-----------+
///            0      4      1                             0
/// ```
///
/// ## 3D: Local indices of points, edges, and faces
///
/// ```text
///                   Points (8 or 20)                   Faces (6)
///    z
///    |           4        15        7
///   ,+--y         @-------@--------@                 +----------------+
/// x'            ,'|              ,'|               ,'|              ,'|
///          12 @'  |         14 ,'  |             ,'  |  ___       ,'  |
///           ,'    |16        ,@    |19         ,'    |,'5,'  [0],'    |
///     5   ,'      @      6 ,'      @         ,'      |~~~     ,'      |
///       @'=======@=======@'        |       +'===============+'  ,'|   |
///       |      13 |      |         |       |   ,'|   |      |   |3|   |
///       |         |      |  11     |       |   |2|   |      |   |,'   |
///    17 |       0 @- - - | @- - - -@       |   |,'   +- - - | +- - - -+
///       @       ,'       @       ,' 3      |       ,'       |       ,'
///       |   8 @'      18 |     ,'          |     ,' [1]  ___|     ,'
///       |   ,'           |   ,@ 10         |   ,'      ,'4,'|   ,'
///       | ,'             | ,'              | ,'        ~~~  | ,'
///       @-------@--------@'                +----------------+'
///     1         9         2
///
///
///                  Edges (12)
///
///                         7
///                 +----------------+
///               ,'|              ,'|
///           4 ,'  |8          6,'  |
///           ,'    |          ,'    |
///         ,'      |   5    ,'      |11
///       +'===============+'        |
///       |         |      |         |
///       |         |      |  3      |
///       |         .- - - | -  - - -+
///      9|       ,'       |       ,'
///       |    0,'         |10   ,'
///       |   ,'           |   ,' 2
///       | ,'             | ,'
///       +----------------+'
///               1
///
pub struct Block {
    group: usize,             // group of all elements in this block
    ndim: usize,              // space dimension
    npoint: usize,            // number of points (4, 8, or 20)
    nedge: usize,             // number of edges (4 or 12)
    nface: usize,             // number of faces (4 or 6)
    coords: Matrix,           // coordinates of points (npoint, ndim)
    point_groups: Vec<usize>, // point groups (npoint)
    edge_groups: Vec<usize>,  // edge groups (nedge)
    face_groups: Vec<usize>,  // face groups (nface)
    num_div: Vec<usize>,      // number of divisions along each dim (ndim)
    weights: Vec<Vec<f64>>,   // weights along each dimension (ndim, nweight)
    sum_weights: Vec<f64>,    // sum of weights along each dimension (ndim)

    // maps side to constraint (num_sides)
    constraints: HashMap<Constraint, ConstraintData>,
}

impl Block {
    pub fn new(group: usize, kind: BlockKind) -> Self {
        let (ndim, npoint, nedge, nface) = match kind {
            BlockKind::Qua4 => (2, 4, 4, 0),
            BlockKind::Qua8 => (2, 8, 4, 0),
            BlockKind::Hex8 => (3, 8, 12, 6),
            BlockKind::Hex20 => (3, 20, 12, 6),
        };
        Block {
            group,
            ndim,
            npoint,
            nedge,
            nface,
            coords: Matrix::new(npoint, ndim),
            point_groups: vec![0; npoint],
            edge_groups: vec![0; nedge],
            face_groups: vec![0; nface],
            num_div: vec![0; ndim],
            weights: vec![Vec::new(); ndim],
            sum_weights: vec![0.0; ndim],
            constraints: HashMap::new(),
        }
    }

    pub fn set_point_2d(&mut self, m: usize, group: usize, x: f64, y: f64) -> &mut Self {
        assert_eq!(self.ndim, 2);
        assert!(m < self.npoint);
        self.point_groups[m] = group;
        self.coords[m][0] = x;
        self.coords[m][1] = y;
        self
    }

    pub fn set_point_3d(&mut self, m: usize, group: usize, x: f64, y: f64, z: f64) -> &mut Self {
        assert_eq!(self.ndim, 3);
        assert!(m < self.npoint);
        self.point_groups[m] = group;
        self.coords[m][0] = x;
        self.coords[m][1] = y;
        self.coords[m][2] = z;
        self
    }

    pub fn set_coords<'a, T, U>(&mut self, coords: &'a T) -> &mut Self
    where
        T: AsArray2D<'a, U>,
        U: 'a + Into<f64>,
    {
        let (nrow, ncol) = coords.size();
        assert_eq!(nrow, self.npoint);
        assert_eq!(ncol, self.ndim);
        for i in 0..nrow {
            for j in 0..ncol {
                self.coords[i][j] = coords.at(i, j).into();
            }
        }
        self
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn new_works() {
        let block = Block::new(1, BlockKind::Qua4);
        assert_eq!(block.group, 1);
        assert_eq!(block.ndim, 2);
        assert_eq!(block.npoint, 4);
        assert_eq!(block.nedge, 4);
        assert_eq!(block.nface, 0);
    }

    #[test]
    fn set_point_2d_works() {
        let mut block = Block::new(1, BlockKind::Qua4);
        block
            .set_point_2d(0, 1, 0.0, 0.0)
            .set_point_2d(1, 2, 2.0, 0.0)
            .set_point_2d(2, 3, 2.0, 2.0)
            .set_point_2d(3, 4, 0.0, 2.0);
        assert_eq!(
            format!("{}", block.coords),
            "┌     ┐\n\
             │ 0 0 │\n\
             │ 2 0 │\n\
             │ 2 2 │\n\
             │ 0 2 │\n\
             └     ┘"
        );
    }

    #[test]
    fn set_coords_works() {
        let mut block = Block::new(1, BlockKind::Qua4);
        #[rustfmt::skip]
        block.set_coords(&[
            [0.0, 0.0],
            [2.0, 0.0],
            [2.0, 2.0],
            [0.0, 2.0],
        ]);
        assert_eq!(
            format!("{}", block.coords),
            "┌     ┐\n\
             │ 0 0 │\n\
             │ 2 0 │\n\
             │ 2 2 │\n\
             │ 0 2 │\n\
             └     ┘"
        );
    }
}
<commit_msg>Add test<commit_after>use crate::AsArray2D;
use russell_lab::Matrix;
use std::collections::HashMap;

pub enum BlockKind {
    Qua4,
    Qua8,
    Hex8,
    Hex20,
}

pub enum Constraint {}

pub struct ConstraintData {
    kind: Constraint,
}

/// Defines a polygon on polyhedron that can be split into smaller shapes
///
/// # Block shape
///
/// ## 2D: Local indices of points and edges
///
/// ```text
///             Points (4 or 8)                        Edges (4)
///   y
///   |        3      6      2                             2
///   +--x      @-----@-----@                        +-----------+
///             |           |                        |           |
///             |           |                        |           |
///           7 @           @ 5                     3|           |1
///             |           |                        |           |
///             |           |                        |           |
///             @-----@-----@                        +-----------+
///            0      4      1                             0
/// ```
///
/// ## 3D: Local indices of points, edges, and faces
///
/// ```text
///                   Points (8 or 20)                   Faces (6)
///    z
///    |           4        15        7
///   ,+--y         @-------@--------@                 +----------------+
/// x'            ,'|              ,'|               ,'|              ,'|
///          12 @'  |         14 ,'  |             ,'  |  ___       ,'  |
///           ,'    |16        ,@    |19         ,'    |,'5,'  [0],'    |
///     5   ,'      @      6 ,'      @         ,'      |~~~     ,'      |
///       @'=======@=======@'        |       +'===============+'  ,'|   |
///       |      13 |      |         |       |   ,'|   |      |   |3|   |
///       |         |      |  11     |       |   |2|   |      |   |,'   |
///    17 |       0 @- - - | @- - - -@       |   |,'   +- - - | +- - - -+
///       @       ,'       @       ,' 3      |       ,'       |       ,'
///       |   8 @'      18 |     ,'          |     ,' [1]  ___|     ,'
///       |   ,'           |   ,@ 10         |   ,'      ,'4,'|   ,'
///       | ,'             | ,'              | ,'        ~~~  | ,'
///       @-------@--------@'                +----------------+'
///     1         9         2
///
///
///                  Edges (12)
///
///                         7
///                 +----------------+
///               ,'|              ,'|
///           4 ,'  |8          6,'  |
///           ,'    |          ,'    |
///         ,'      |   5    ,'      |11
///       +'===============+'        |
///       |         |      |         |
///       |         |      |  3      |
///       |         .- - - | -  - - -+
///      9|       ,'       |       ,'
///       |    0,'         |10   ,'
///       |   ,'           |   ,' 2
///       | ,'             | ,'
///       +----------------+'
///               1
///
pub struct Block {
    group: usize,             // group of all elements in this block
    ndim: usize,              // space dimension
    npoint: usize,            // number of points (4, 8, or 20)
    nedge: usize,             // number of edges (4 or 12)
    nface: usize,             // number of faces (4 or 6)
    coords: Matrix,           // coordinates of points (npoint, ndim)
    point_groups: Vec<usize>, // point groups (npoint)
    edge_groups: Vec<usize>,  // edge groups (nedge)
    face_groups: Vec<usize>,  // face groups (nface)
    num_div: Vec<usize>,      // number of divisions along each dim (ndim)
    weights: Vec<Vec<f64>>,   // weights along each dimension (ndim, nweight)
    sum_weights: Vec<f64>,    // sum of weights along each dimension (ndim)

    // maps side to constraint (num_sides)
    constraints: HashMap<Constraint, ConstraintData>,
}

impl Block {
    pub fn new(group: usize, kind: BlockKind) -> Self {
        let (ndim, npoint, nedge, nface) = match kind {
            BlockKind::Qua4 => (2, 4, 4, 0),
            BlockKind::Qua8 => (2, 8, 4, 0),
            BlockKind::Hex8 => (3, 8, 12, 6),
            BlockKind::Hex20 => (3, 20, 12, 6),
        };
        Block {
            group,
            ndim,
            npoint,
            nedge,
            nface,
            coords: Matrix::new(npoint, ndim),
            point_groups: vec![0; npoint],
            edge_groups: vec![0; nedge],
            face_groups: vec![0; nface],
            num_div: vec![0; ndim],
            weights: vec![Vec::new(); ndim],
            sum_weights: vec![0.0; ndim],
            constraints: HashMap::new(),
        }
    }

    pub fn set_point_2d(&mut self, m: usize, group: usize, x: f64, y: f64) -> &mut Self {
        assert_eq!(self.ndim, 2);
        assert!(m < self.npoint);
        self.point_groups[m] = group;
        self.coords[m][0] = x;
        self.coords[m][1] = y;
        self
    }

    pub fn set_point_3d(&mut self, m: usize, group: usize, x: f64, y: f64, z: f64) -> &mut Self {
        assert_eq!(self.ndim, 3);
        assert!(m < self.npoint);
        self.point_groups[m] = group;
        self.coords[m][0] = x;
        self.coords[m][1] = y;
        self.coords[m][2] = z;
        self
    }

    pub fn set_coords<'a, T, U>(&mut self, coords: &'a T) -> &mut Self
    where
        T: AsArray2D<'a, U>,
        U: 'a + Into<f64>,
    {
        let (nrow, ncol) = coords.size();
        assert_eq!(nrow, self.npoint);
        assert_eq!(ncol, self.ndim);
        for i in 0..nrow {
            for j in 0..ncol {
                self.coords[i][j] = coords.at(i, j).into();
            }
        }
        self
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn new_works() {
        let block = Block::new(1, BlockKind::Qua4);
        assert_eq!(block.group, 1);
        assert_eq!(block.ndim, 2);
        assert_eq!(block.npoint, 4);
        assert_eq!(block.nedge, 4);
        assert_eq!(block.nface, 0);
    }

    #[test]
    fn set_point_2d_works() {
        let mut block = Block::new(1, BlockKind::Qua4);
        block
            .set_point_2d(0, 1, 0.0, 0.0)
            .set_point_2d(1, 2, 2.0, 0.0)
            .set_point_2d(2, 3, 2.0, 2.0)
            .set_point_2d(3, 4, 0.0, 2.0);
        assert_eq!(
            format!("{}", block.coords),
            "┌     ┐\n\
             │ 0 0 │\n\
             │ 2 0 │\n\
             │ 2 2 │\n\
             │ 0 2 │\n\
             └     ┘"
        );
    }

    #[test]
    fn set_point_3d_works() {
        let mut block = Block::new(1, BlockKind::Hex8);
        block
            .set_point_3d(0, 1, 0.0, 0.0, 0.0)
            .set_point_3d(1, 2, 2.0, 0.0, 0.0)
            .set_point_3d(2, 3, 2.0, 2.0, 0.0)
            .set_point_3d(3, 4, 0.0, 2.0, 0.0)
            .set_point_3d(4, 1, 0.0, 0.0, 2.0)
            .set_point_3d(5, 2, 2.0, 0.0, 2.0)
            .set_point_3d(6, 3, 2.0, 2.0, 2.0)
            .set_point_3d(7, 4, 0.0, 2.0, 2.0);
        assert_eq!(
            format!("{}", block.coords),
            "┌       ┐\n\
             │ 0 0 0 │\n\
             │ 2 0 0 │\n\
             │ 2 2 0 │\n\
             │ 0 2 0 │\n\
             │ 0 0 2 │\n\
             │ 2 0 2 │\n\
             │ 2 2 2 │\n\
             │ 0 2 2 │\n\
             └       ┘"
        );
    }

    #[test]
    fn set_coords_works() {
        let mut block = Block::new(1, BlockKind::Qua4);
        #[rustfmt::skip]
        block.set_coords(&[
            [0.0, 0.0],
            [2.0, 0.0],
            [2.0, 2.0],
            [0.0, 2.0],
        ]);
        assert_eq!(
            format!("{}", block.coords),
            "┌     ┐\n\
             │ 0 0 │\n\
             │ 2 0 │\n\
             │ 2 2 │\n\
             │ 0 2 │\n\
             └     ┘"
        );
    }
}
<|endoftext|>


<commit_before>use libc;
use std::cell::Cell;

use attributes::Attribute;
use coord_units::CoordUnits;
use drawing_ctx::RsvgDrawingCtx;
use handle::RsvgHandle;
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::parse;
use property_bag::PropertyBag;
use state::ComputedValues;

coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);

struct NodeClipPath {
    units: Cell<ClipPathUnits>,
}

impl NodeClipPath {
    fn new() -> NodeClipPath {
        NodeClipPath {
            units: Cell::new(ClipPathUnits::default()),
        }
    }
}

impl NodeTrait for NodeClipPath {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::ClipPathUnits => {
                    self.units.set(parse("clipPathUnits", value, (), None)?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(&self, _: &RsvgNode, _: *mut RsvgDrawingCtx, _: &ComputedValues, _: i32, _: bool) {
        // nothing; clip paths are handled specially
    }

    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        unreachable!();
    }
}

#[no_mangle]
pub extern "C" fn rsvg_node_clip_path_new(
    _: *const libc::c_char,
    raw_parent: *const RsvgNode,
) -> *const RsvgNode {
    boxed_node_new(
        NodeType::ClipPath,
        raw_parent,
        Box::new(NodeClipPath::new()),
    )
}

#[no_mangle]
pub extern "C" fn rsvg_node_clip_path_get_units(raw_node: *const RsvgNode) -> CoordUnits {
    assert!(!raw_node.is_null());
    let node: &RsvgNode = unsafe { &*raw_node };

    let mut units = ClipPathUnits::default();

    node.with_impl(|clip_path: &NodeClipPath| {
        units = clip_path.units.get();
    });

    CoordUnits::from(units)
}
<commit_msg>NodeClipPath.get_units(): New function<commit_after>use libc;
use std::cell::Cell;

use attributes::Attribute;
use coord_units::CoordUnits;
use drawing_ctx::RsvgDrawingCtx;
use handle::RsvgHandle;
use node::{boxed_node_new, NodeResult, NodeTrait, NodeType, RsvgCNodeImpl, RsvgNode};
use parsers::parse;
use property_bag::PropertyBag;
use state::ComputedValues;

coord_units!(ClipPathUnits, CoordUnits::UserSpaceOnUse);

struct NodeClipPath {
    units: Cell<ClipPathUnits>,
}

impl NodeClipPath {
    fn new() -> NodeClipPath {
        NodeClipPath {
            units: Cell::new(ClipPathUnits::default()),
        }
    }

    pub fn get_units(&self) -> ClipPathUnits {
        self.units.get()
    }
}

impl NodeTrait for NodeClipPath {
    fn set_atts(&self, _: &RsvgNode, _: *const RsvgHandle, pbag: &PropertyBag) -> NodeResult {
        for (_key, attr, value) in pbag.iter() {
            match attr {
                Attribute::ClipPathUnits => {
                    self.units.set(parse("clipPathUnits", value, (), None)?)
                }

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(&self, _: &RsvgNode, _: *mut RsvgDrawingCtx, _: &ComputedValues, _: i32, _: bool) {
        // nothing; clip paths are handled specially
    }

    fn get_c_impl(&self) -> *const RsvgCNodeImpl {
        unreachable!();
    }
}

#[no_mangle]
pub extern "C" fn rsvg_node_clip_path_new(
    _: *const libc::c_char,
    raw_parent: *const RsvgNode,
) -> *const RsvgNode {
    boxed_node_new(
        NodeType::ClipPath,
        raw_parent,
        Box::new(NodeClipPath::new()),
    )
}

#[no_mangle]
pub extern "C" fn rsvg_node_clip_path_get_units(raw_node: *const RsvgNode) -> CoordUnits {
    assert!(!raw_node.is_null());
    let node: &RsvgNode = unsafe { &*raw_node };

    let mut units = ClipPathUnits::default();

    node.with_impl(|clip_path: &NodeClipPath| {
        units = clip_path.get_units();
    });

    CoordUnits::from(units)
}
<|endoftext|>


<commit_before>use crate::spec::EntitySpawn;
use crate::{Location, Terrain};
use calx::{tiled, CellVector, FromPrefab, IntoPrefab};
use euclid::vec2;
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::convert::TryFrom;
use std::error::Error;
use std::fmt;
use std::str::FromStr;

pub type Prefab = HashMap<CellVector, (Terrain, Vec<EntitySpawn>)>;

const LEGEND_ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                               abcdefghijklmnopqrstuvwxyz\
                               αβγδεζηθικλμξπρστφχψω\
                               ΓΔΛΞΠΣΦΨΩ\
                               БГҐДЂЃЄЖЗЙЛЉЊПЎФЦЧЏШЩЪЭЮЯ\
                               àèòùáêõýþâìúãíäîåæçéóëïðñôûöøüÿ\
                               ÀÈÒÙÁÊÕÝÞÂÌÚÃÉÓÄÍÅÆÇËÎÔÏÐÑÖØÛßÜ";

const TILED_TILE_WIDTH: f32 = 16.0;
const TILED_TILE_HEIGHT: f32 = 16.0;

/// Types that can be described in pseudo-natural language.
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Parseable<T>(pub T);

serde_plain::derive_deserialize_from_str!(Parseable<(Terrain, Vec<EntitySpawn>)>, "parseable");
serde_plain::derive_serialize_from_display!(Parseable<(Terrain, Vec<EntitySpawn>)>);

impl std::str::FromStr for Parseable<(Terrain, Vec<EntitySpawn>)> {
    type Err = Box<dyn std::error::Error>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split(',').map(|s| s.trim()).collect();
        if parts.is_empty() {
            Err("No terrain set")?;
        }

        let terrain = Terrain::from_str(parts[0])?;
        let mut spawns = Vec::new();
        for spawn in parts.iter().skip(1) {
            spawns.push(EntitySpawn::from_str(spawn)?);
        }

        Ok(Parseable((terrain, spawns)))
    }
}

impl std::fmt::Display for Parseable<(Terrain, Vec<EntitySpawn>)> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let (ref terrain, ref spawns) = self.0;
        write!(f, "{}", terrain.name())?;
        for c in spawns {
            write!(f, ", {}", c)?;
        }
        Ok(())
    }
}

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct MapPatch {
    pub map: String,
    pub legend: BTreeMap<char, Parseable<(Terrain, Vec<EntitySpawn>)>>,
}

impl MapPatch {
    /// Construct a `MapPatch` from cell data.
    ///
    /// Will fail if there are so many unique cell types that the legend alphabet gets exhausted.
    /// Does not preserve offset of the input, the result will have origin at the top left corner
    /// of the bounding box containing all the map cells.
    pub fn new(
        cells: impl IntoIterator<Item = (CellVector, (Terrain, Vec<EntitySpawn>))>,
    ) -> Result<MapPatch, Box<dyn Error>> {
        let chars_f = move |x: &(Terrain, Vec<EntitySpawn>)| {
            let &(ref t, ref e) = x;
            if e.is_empty() {
                t.preferred_map_chars()
            } else {
                ""
            }
        };

        let mut legend_builder = calx::LegendBuilder::new(LEGEND_ALPHABET.to_string(), chars_f);

        let mut prefab = HashMap::new();
        for (k, v) in cells.into_iter() {
            if let Ok(c) = legend_builder.add(&v) {
                prefab.insert(k, c);
            } else {
                Err("Unable to build legend, scene too complex?")?;
            }
        }

        let map = calx::DenseTextMap::from_prefab(&prefab).0;
        let legend = legend_builder
            .legend
            .into_iter()
            .map(|(c, k)| (c, Parseable(k)))
            .collect();

        Ok(MapPatch { map, legend })
    }

    pub fn iter(&self) -> impl Iterator<Item = (CellVector, (Terrain, Vec<EntitySpawn>))> + '_ {
        calx::DenseTextMap(&self.map)
            .into_prefab::<HashMap<CellVector, char>>()
            .unwrap()
            .into_iter()
            .map(move |(p, c)| (p, (self.legend.get(&c).unwrap().0).clone()))
    }
}

impl fmt::Display for MapPatch {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{}", outline::into_outline(self).unwrap())
    }
}

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct PatchData {
    pub offset: Location,

    #[serde(flatten)]
    pub patch: MapPatch,
}

impl fmt::Display for PatchData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{}", outline::into_outline(self).unwrap())
    }
}

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct WorldData {
    pub patches: Vec<PatchData>,
}

impl fmt::Display for WorldData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{}", outline::into_outline(self).unwrap())
    }
}

impl TryFrom<tiled::Map> for WorldData {
    type Error = Box<dyn Error>;

    fn try_from(tiled: tiled::Map) -> Result<Self, Self::Error> {
        // Find layer with magic name "surface" to fix z level with, otherwise z=0 is top layer and
        // it counts down from there.
        let starting_z = tiled
            .layers
            .iter()
            .filter(|a| a.is_tile_layer())
            .enumerate()
            .find(|(_, a)| a.name().to_lowercase() == "surface")
            .map(|(i, _)| (i as i32))
            .unwrap_or(0);

        let mut terrain_map = HashMap::new();
        let mut spawn_map = HashMap::new();

        // Process Tiled layers
        let mut z = starting_z;
        for layer in tiled.layers.iter().rev() {
            let loc = Location::new(0, 0, z as i16);

            if let Some(i) = layer.iter_tiles() {
                for (pos, t) in i {
                    let loc = loc + vec2(pos.x, pos.y);
                    if let Some(t) = tiled_to_terrain(t) {
                        terrain_map.insert(loc, t);
                    }
                }

                z -= 1;
            }

            if let Some(i) = layer.iter_objects() {
                for o in i {
                    // XXX: Tiled makes Y be off by one, has upwards-pointing Y-axis?
                    let loc = loc
                        + vec2(
                            (o.x / TILED_TILE_WIDTH).round() as i32,
                            (o.y / TILED_TILE_HEIGHT).round() as i32 - 1,
                        );
                    spawn_map.insert(loc, tiled_to_spawn(o)?);
                }
            }
        }

        // Construct intermediate storage layers.
        struct Layer {
            // Extents of the terrain data, used to compute offset for the segment.
            min_x: i32,
            min_y: i32,
            cells: HashMap<CellVector, (Terrain, Vec<EntitySpawn>)>,
        }

        impl Default for Layer {
            fn default() -> Self {
                Layer {
                    min_x: std::i32::MAX,
                    min_y: std::i32::MAX,
                    cells: Default::default(),
                }
            }
        }

        let mut layers = BTreeMap::new();
        for (loc, c) in terrain_map {
            let layer = layers.entry(loc.z).or_insert(Layer::default());
            let pos = CellVector::new(loc.x as i32, loc.y as i32);
            layer.min_x = layer.min_x.min(pos.x);
            layer.min_y = layer.min_y.min(pos.y);
            layer.cells.insert(pos, (c, Vec::new() as Vec<EntitySpawn>));
        }

        for (loc, c) in spawn_map {
            let layer = layers.entry(loc.z).or_insert(Layer::default());
            let pos = CellVector::new(loc.x as i32, loc.y as i32);
            if let Some(cell) = layer.cells.get_mut(&pos) {
                cell.1.push(c);
            } else {
                Err(format!("Object spawn {}: {:?} outside terrain", c, loc))?;
            }
        }

        // Construct WorldData instance from intermediate data.

        let mut patches = Vec::new();
        for (z, layer) in layers.into_iter().rev() {
            let offset = Location::new(layer.min_x as i16, layer.min_y as i16, z as i16);
            let patch = MapPatch::new(layer.cells.into_iter())?;
            patches.push(PatchData { offset, patch });
        }

        Ok(WorldData { patches })
    }
}

fn tiled_to_terrain(tiled_id: u32) -> Option<Terrain> {
    /// Hardcoded tileset used in Tiled maps. Edit as needed.
    const TILED_TILES: [Terrain; 16] = [
        // FIXME: Only have valid terrains in the list, keep this simple...
        Terrain::Empty,
        Terrain::Empty,
        Terrain::Ground,
        Terrain::Water,
        Terrain::Empty, // TODO: Monolith terrain
        Terrain::Tree,
        Terrain::Wall,
        Terrain::Rock,
        Terrain::Window,
        Terrain::Door,
        Terrain::Downstairs,
        Terrain::Upstairs,
        Terrain::Grass,
        Terrain::Shallows,
        Terrain::Sand,
        Terrain::Empty, // TODO: Mountain face terrain
    ];
    if let Some(&t) = TILED_TILES.get(tiled_id as usize) {
        if t != Terrain::Empty {
            Some(t)
        } else {
            None
        }
    } else {
        None
    }
}

fn tiled_to_spawn(object: &tiled::Object) -> Result<EntitySpawn, Box<dyn Error>> {
    const TILED_SPAWNS_OFFSET: usize = 129;
    const TILED_SPAWNS: [&str; 4] = ["player", "dreg", "ooze", "sword"];

    if !object.name.is_empty() {
        return Ok(EntitySpawn::from_str(&object.name)?);
    }

    let gid = object.gid as usize;

    if gid >= TILED_SPAWNS_OFFSET {
        if let Some(s) = TILED_SPAWNS.get(gid - TILED_SPAWNS_OFFSET) {
            return Ok(EntitySpawn::from_str(s)?);
        }
    }
    Err("Bad spawn")?
}

#[cfg(test)]
mod test {
    #[test]
    fn test_legend_parseable() {
        use super::Parseable;
        use crate::spec::EntitySpawn;
        use crate::Terrain;
        use std::str::FromStr;

        type LegendEntry = Parseable<(Terrain, Vec<EntitySpawn>)>;

        assert!(LegendEntry::from_str("").is_err());

        assert_eq!(
            LegendEntry::from_str("grass").unwrap(),
            Parseable((Terrain::Grass, Vec::new()))
        );
        assert_eq!(
            LegendEntry::from_str("grass, sword").unwrap(),
            Parseable((
                Terrain::Grass,
                vec![EntitySpawn::from_str("sword").unwrap()]
            ))
        );
        assert_eq!(
            LegendEntry::from_str("sand, scroll of lightning, snake").unwrap(),
            Parseable((
                Terrain::Sand,
                vec![
                    EntitySpawn::from_str("scroll of lightning").unwrap(),
                    EntitySpawn::from_str("snake").unwrap()
                ]
            ))
        );
        assert_eq!(
            format!(
                "{}",
                Parseable((
                    Terrain::Sand,
                    vec![
                        EntitySpawn::from_str("scroll of lightning").unwrap(),
                        EntitySpawn::from_str("snake").unwrap()
                    ]
                ))
            ),
            "sand, scroll of lightning, snake"
        );
    }
}
<commit_msg>Opinionated map loading, split big maps into sectors<commit_after>use crate::spec::EntitySpawn;
use crate::{Location, Sector, Terrain};
use calx::{tiled, CellVector, FromPrefab, IntoPrefab};
use euclid::vec2;
use serde_derive::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap};
use std::convert::TryFrom;
use std::error::Error;
use std::fmt;
use std::str::FromStr;

pub type Prefab = HashMap<CellVector, (Terrain, Vec<EntitySpawn>)>;

const LEGEND_ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                               abcdefghijklmnopqrstuvwxyz\
                               αβγδεζηθικλμξπρστφχψω\
                               ΓΔΛΞΠΣΦΨΩ\
                               БГҐДЂЃЄЖЗЙЛЉЊПЎФЦЧЏШЩЪЭЮЯ\
                               àèòùáêõýþâìúãíäîåæçéóëïðñôûöøüÿ\
                               ÀÈÒÙÁÊÕÝÞÂÌÚÃÉÓÄÍÅÆÇËÎÔÏÐÑÖØÛßÜ";

const TILED_TILE_WIDTH: f32 = 16.0;
const TILED_TILE_HEIGHT: f32 = 16.0;

/// Types that can be described in pseudo-natural language.
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct Parseable<T>(pub T);

serde_plain::derive_deserialize_from_str!(Parseable<(Terrain, Vec<EntitySpawn>)>, "parseable");
serde_plain::derive_serialize_from_display!(Parseable<(Terrain, Vec<EntitySpawn>)>);

impl std::str::FromStr for Parseable<(Terrain, Vec<EntitySpawn>)> {
    type Err = Box<dyn std::error::Error>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split(',').map(|s| s.trim()).collect();
        if parts.is_empty() {
            Err("No terrain set")?;
        }

        let terrain = Terrain::from_str(parts[0])?;
        let mut spawns = Vec::new();
        for spawn in parts.iter().skip(1) {
            spawns.push(EntitySpawn::from_str(spawn)?);
        }

        Ok(Parseable((terrain, spawns)))
    }
}

impl std::fmt::Display for Parseable<(Terrain, Vec<EntitySpawn>)> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let (ref terrain, ref spawns) = self.0;
        write!(f, "{}", terrain.name())?;
        for c in spawns {
            write!(f, ", {}", c)?;
        }
        Ok(())
    }
}

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct MapPatch {
    pub map: String,
    pub legend: BTreeMap<char, Parseable<(Terrain, Vec<EntitySpawn>)>>,
}

impl MapPatch {
    /// Construct a `MapPatch` from cell data.
    ///
    /// Will fail if there are so many unique cell types that the legend alphabet gets exhausted.
    /// Does not preserve offset of the input, the result will have origin at the top left corner
    /// of the bounding box containing all the map cells.
    pub fn new(
        cells: impl IntoIterator<Item = (CellVector, (Terrain, Vec<EntitySpawn>))>,
    ) -> Result<MapPatch, Box<dyn Error>> {
        let chars_f = move |x: &(Terrain, Vec<EntitySpawn>)| {
            let &(ref t, ref e) = x;
            if e.is_empty() {
                t.preferred_map_chars()
            } else {
                ""
            }
        };

        let mut legend_builder = calx::LegendBuilder::new(LEGEND_ALPHABET.to_string(), chars_f);

        let mut prefab = HashMap::new();
        for (k, v) in cells.into_iter() {
            if let Ok(c) = legend_builder.add(&v) {
                prefab.insert(k, c);
            } else {
                Err("Unable to build legend, scene too complex?")?;
            }
        }

        let map = calx::DenseTextMap::from_prefab(&prefab).0;
        let legend = legend_builder
            .legend
            .into_iter()
            .map(|(c, k)| (c, Parseable(k)))
            .collect();

        Ok(MapPatch { map, legend })
    }

    pub fn iter(&self) -> impl Iterator<Item = (CellVector, (Terrain, Vec<EntitySpawn>))> + '_ {
        calx::DenseTextMap(&self.map)
            .into_prefab::<HashMap<CellVector, char>>()
            .unwrap()
            .into_iter()
            .map(move |(p, c)| (p, (self.legend.get(&c).unwrap().0).clone()))
    }
}

impl fmt::Display for MapPatch {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{}", outline::into_outline(self).unwrap())
    }
}

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct PatchData {
    pub offset: Location,

    #[serde(flatten)]
    pub patch: MapPatch,
}

impl fmt::Display for PatchData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{}", outline::into_outline(self).unwrap())
    }
}

#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct WorldData {
    pub patches: Vec<PatchData>,
}

impl fmt::Display for WorldData {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(f, "{}", outline::into_outline(self).unwrap())
    }
}

impl TryFrom<tiled::Map> for WorldData {
    type Error = Box<dyn Error>;

    fn try_from(tiled: tiled::Map) -> Result<Self, Self::Error> {
        // Find layer with magic name "surface" to fix z level with, otherwise z=0 is top layer and
        // it counts down from there.
        let starting_z = tiled
            .layers
            .iter()
            .filter(|a| a.is_tile_layer())
            .enumerate()
            .find(|(_, a)| a.name().to_lowercase() == "surface")
            .map(|(i, _)| (i as i32))
            .unwrap_or(0);

        let mut terrain_map = HashMap::new();
        let mut spawn_map = HashMap::new();

        // Process Tiled layers
        let mut z = starting_z;
        for layer in tiled.layers.iter().rev() {
            let loc = Location::new(0, 0, z as i16);

            if let Some(i) = layer.iter_tiles() {
                for (pos, t) in i {
                    let loc = loc + vec2(pos.x, pos.y);
                    if let Some(t) = tiled_to_terrain(t) {
                        terrain_map.insert(loc, t);
                    }
                }

                z -= 1;
            }

            if let Some(i) = layer.iter_objects() {
                for o in i {
                    // XXX: Tiled makes Y be off by one, has upwards-pointing Y-axis?
                    let loc = loc
                        + vec2(
                            (o.x / TILED_TILE_WIDTH).round() as i32,
                            (o.y / TILED_TILE_HEIGHT).round() as i32 - 1,
                        );
                    spawn_map.insert(loc, tiled_to_spawn(o)?);
                }
            }
        }

        // Construct intermediate storage layers.
        struct Layer {
            // Extents of the terrain data, used to compute offset for the segment.
            min_x: i32,
            min_y: i32,
            cells: HashMap<CellVector, (Terrain, Vec<EntitySpawn>)>,
        }

        impl Default for Layer {
            fn default() -> Self {
                Layer {
                    min_x: std::i32::MAX,
                    min_y: std::i32::MAX,
                    cells: Default::default(),
                }
            }
        }

        let mut layers = BTreeMap::new();
        for (loc, c) in terrain_map {
            let sector = Sector::from(loc);
            let pos = Location::from(sector).v2_at(loc).unwrap();

            let layer = layers.entry(sector).or_insert(Layer::default());
            layer.min_x = layer.min_x.min(pos.x);
            layer.min_y = layer.min_y.min(pos.y);
            layer.cells.insert(pos, (c, Vec::new() as Vec<EntitySpawn>));
        }

        for (loc, c) in spawn_map {
            let sector = Sector::from(loc);
            let pos = Location::from(sector).v2_at(loc).unwrap();

            let layer = layers.entry(sector).or_insert(Layer::default());
            if let Some(cell) = layer.cells.get_mut(&pos) {
                cell.1.push(c);
            } else {
                Err(format!("Object spawn {}: {:?} outside terrain", c, loc))?;
            }
        }

        // Construct WorldData instance from intermediate data.

        let mut patches = Vec::new();
        for (sector, layer) in layers.into_iter() {
            let offset = Location::from(sector) + vec2(layer.min_x, layer.min_y);
            let patch = MapPatch::new(layer.cells.into_iter())?;
            patches.push(PatchData { offset, patch });
        }

        Ok(WorldData { patches })
    }
}

fn tiled_to_terrain(tiled_id: u32) -> Option<Terrain> {
    /// Hardcoded tileset used in Tiled maps. Edit as needed.
    const TILED_TILES: [Terrain; 16] = [
        // FIXME: Only have valid terrains in the list, keep this simple...
        Terrain::Empty,
        Terrain::Empty,
        Terrain::Ground,
        Terrain::Water,
        Terrain::Empty, // TODO: Monolith terrain
        Terrain::Tree,
        Terrain::Wall,
        Terrain::Rock,
        Terrain::Window,
        Terrain::Door,
        Terrain::Downstairs,
        Terrain::Upstairs,
        Terrain::Grass,
        Terrain::Shallows,
        Terrain::Sand,
        Terrain::Empty, // TODO: Mountain face terrain
    ];
    if let Some(&t) = TILED_TILES.get(tiled_id as usize) {
        if t != Terrain::Empty {
            Some(t)
        } else {
            None
        }
    } else {
        None
    }
}

fn tiled_to_spawn(object: &tiled::Object) -> Result<EntitySpawn, Box<dyn Error>> {
    const TILED_SPAWNS_OFFSET: usize = 129;
    const TILED_SPAWNS: [&str; 4] = ["player", "dreg", "ooze", "sword"];

    if !object.name.is_empty() {
        return Ok(EntitySpawn::from_str(&object.name)?);
    }

    let gid = object.gid as usize;

    if gid >= TILED_SPAWNS_OFFSET {
        if let Some(s) = TILED_SPAWNS.get(gid - TILED_SPAWNS_OFFSET) {
            return Ok(EntitySpawn::from_str(s)?);
        }
    }
    Err("Bad spawn")?
}

#[cfg(test)]
mod test {
    #[test]
    fn test_legend_parseable() {
        use super::Parseable;
        use crate::spec::EntitySpawn;
        use crate::Terrain;
        use std::str::FromStr;

        type LegendEntry = Parseable<(Terrain, Vec<EntitySpawn>)>;

        assert!(LegendEntry::from_str("").is_err());

        assert_eq!(
            LegendEntry::from_str("grass").unwrap(),
            Parseable((Terrain::Grass, Vec::new()))
        );
        assert_eq!(
            LegendEntry::from_str("grass, sword").unwrap(),
            Parseable((
                Terrain::Grass,
                vec![EntitySpawn::from_str("sword").unwrap()]
            ))
        );
        assert_eq!(
            LegendEntry::from_str("sand, scroll of lightning, snake").unwrap(),
            Parseable((
                Terrain::Sand,
                vec![
                    EntitySpawn::from_str("scroll of lightning").unwrap(),
                    EntitySpawn::from_str("snake").unwrap()
                ]
            ))
        );
        assert_eq!(
            format!(
                "{}",
                Parseable((
                    Terrain::Sand,
                    vec![
                        EntitySpawn::from_str("scroll of lightning").unwrap(),
                        EntitySpawn::from_str("snake").unwrap()
                    ]
                ))
            ),
            "sand, scroll of lightning, snake"
        );
    }
}
<|endoftext|>