<commit_before>use std::hash::Hasher;
use std::hash::sip::SipHasher;
use std::io::{File, IoError};
use std::io;
use std::os::args;
use std::str;
use term::color::YELLOW;

use core::{Package, PackageSet, Target};
use util;
use util::{CargoResult, ChainError, ProcessBuilder, internal, human, CargoError};
use util::{Config, TaskPool, DependencyQueue, Fresh, Dirty, Freshness};

type Args = Vec<String>;

struct Context<'a, 'b> {
    dest: &'a Path,
    deps_dir: &'a Path,
    primary: bool,
    rustc_version: &'a str,
    config: &'b mut Config<'b>
}

type Job = proc():Send -> CargoResult<()>;

// This is a temporary assert that ensures the consistency of the arguments
// given the current limitations of Cargo. The long term fix is to have each
// Target know the absolute path to the build location.
fn uniq_target_dest<'a>(targets: &[&'a Target]) -> Option<&'a str> {
    let mut curr: Option<Option<&str>> = None;

    for t in targets.iter() {
        let dest = t.get_profile().get_dest();

        match curr {
            Some(curr) => assert!(curr == dest),
            None => curr = Some(dest)
        }
    }

    curr.unwrap()
}

pub fn compile_targets<'a>(env: &str, targets: &[&Target], pkg: &Package,
                           deps: &PackageSet,
                           config: &'a mut Config<'a>) -> CargoResult<()> {

    if targets.is_empty() {
        return Ok(());
    }

    debug!("compile_targets; targets={}; pkg={}; deps={}", targets, pkg, deps);

    let path_fragment = uniq_target_dest(targets);
    let target_dir = pkg.get_absolute_target_dir().join(path_fragment.unwrap_or(""));
    let deps_target_dir = target_dir.join("deps");

    let output = try!(util::process("rustc").arg("-v").exec_with_output());
    let rustc_version = str::from_utf8(output.output.as_slice()).unwrap();

    // First ensure that the destination directory exists
    debug!("creating target dir; path={}", target_dir.display());

    try!(mk_target(&target_dir).chain_error(||
        internal(format!("Couldn't create the target directory for {} at {}",
                 pkg.get_name(), target_dir.display()))));

    try!(mk_target(&deps_target_dir).chain_error(||
        internal(format!("Couldn't create the directory for dependencies for {} at {}",
                 pkg.get_name(), deps_target_dir.display()))));

    let mut cx = Context {
        dest: &deps_target_dir,
        deps_dir: &deps_target_dir,
        primary: false,
        rustc_version: rustc_version.as_slice(),
        config: config
    };

    // Build up a list of pending jobs, each of which represent compiling a
    // particular package. No actual work is executed as part of this, that's
    // all done later as part of the `execute` function which will run
    // everything in order with proper parallelism.
    let mut jobs = Vec::new();
    for dep in deps.iter() {
        // Only compile lib targets for dependencies
        let targets = dep.get_targets().iter().filter(|target| {
            target.is_lib() && target.get_profile().get_env() == env
        }).collect::<Vec<&Target>>();

        jobs.push((dep, try!(compile(targets.as_slice(), dep, &mut cx))));
    }

    cx.primary = true;
    cx.dest = &target_dir;
    jobs.push((pkg, try!(compile(targets, pkg, &mut cx))));

    // Now that we've figured out everything that we're going to do, do it!
    execute(cx.config, jobs)
}

fn compile(targets: &[&Target], pkg: &Package,
           cx: &mut Context) -> CargoResult<(Freshness, Job)> {
    debug!("compile_pkg; pkg={}; targets={}", pkg, pkg.get_targets());

    if targets.is_empty() {
        return Ok((Fresh, proc() Ok(())))
    }

    // First check to see if this package is fresh.
    //
    // Note that we're compiling things in topological order, so if nothing has
    // been built up to this point and we're fresh, then we can safely skip
    // recompilation. If anything has previously been rebuilt, it may have been
    // a dependency of ours, so just go ahead and rebuild ourselves.
    //
    // This is not quite accurate, we should only trigger forceful
    // recompilations for downstream dependencies of ourselves, not everyone
    // compiled afterwards.a
    //
    // TODO: Figure out how this works with targets
    let fingerprint_loc = cx.dest.join(format!(".{}.fingerprint",
                                               pkg.get_name()));

    let (is_fresh, fingerprint) = try!(is_fresh(pkg, &fingerprint_loc, cx,
                                                targets));

    let mut cmds = Vec::new();

    // TODO: Should this be on the target or the package?
    for build_cmd in pkg.get_manifest().get_build().iter() {
        cmds.push(compile_custom(pkg, build_cmd.as_slice(), cx));
    }

    // After the custom command has run, execute rustc for all targets of our
    // package.
    for &target in targets.iter() {
        cmds.push(rustc(&pkg.get_root(), target, cx));
    }

    cmds.push(proc() {
        // If this job runs, then everything has successfully compiled, so write
        // our new fingerprint to the relevant location to prevent
        // recompilations in the future.
        try!(File::create(&fingerprint_loc).write_str(fingerprint.as_slice()));
        Ok(())
    });

    // TODO: this job itself may internally be parallel, but we're hiding that
    //       currently. How to expose the parallelism among a single target?
    Ok((if is_fresh {Fresh} else {Dirty}, proc() {
        for cmd in cmds.move_iter() {
            try!(cmd());
        }
        Ok(())
    }))
}

fn is_fresh(dep: &Package, loc: &Path,
            cx: &mut Context, targets: &[&Target]) -> CargoResult<(bool, String)>
{
    let new_pkg_fingerprint = format!("{}{}", cx.rustc_version,
                                  try!(dep.get_fingerprint(cx.config)));

    let new_fingerprint = fingerprint(new_pkg_fingerprint, hash_targets(targets));

    let mut file = match File::open(loc) {
        Ok(file) => file,
        Err(..) => return Ok((false, new_fingerprint)),
    };

    let old_fingerprint = try!(file.read_to_str());

    log!(5, "old fingerprint: {}", old_fingerprint);
    log!(5, "new fingerprint: {}", new_fingerprint);

    Ok((old_fingerprint == new_fingerprint, new_fingerprint))
}

fn hash_targets(targets: &[&Target]) -> u64 {
    let hasher = SipHasher::new_with_keys(0,0);
    let targets = targets.iter().map(|t| (*t).clone()).collect::<Vec<Target>>();
    hasher.hash(&targets)
}

fn fingerprint(package: String, profiles: u64) -> String {
    let hasher = SipHasher::new_with_keys(0,0);
    util::to_hex(hasher.hash(&(package, profiles)))
}

fn mk_target(target: &Path) -> Result<(), IoError> {
    io::fs::mkdir_recursive(target, io::UserRWX)
}

fn compile_custom(pkg: &Package, cmd: &str,
                  cx: &Context) -> Job {
    // FIXME: this needs to be smarter about splitting
    let mut cmd = cmd.split(' ');
    let mut p = util::process(cmd.next().unwrap())
                     .cwd(pkg.get_root())
                     .env("OUT_DIR", Some(cx.dest.as_str().expect("non-UTF8 dest path")))
                     .env("DEPS_DIR", Some(cx.dest.join(cx.deps_dir)
                                             .as_str().expect("non-UTF8 deps path")));
    for arg in cmd {
        p = p.arg(arg);
    }
    proc() p.exec_with_output().map(|_| ()).map_err(|e| e.mark_human())
}

fn rustc(root: &Path, target: &Target, cx: &mut Context) -> Job {
    let crate_types = target.rustc_crate_types();

    log!(5, "root={}; target={}; crate_types={}; dest={}; deps={}; verbose={}",
         root.display(), target, crate_types, cx.dest.display(),
         cx.deps_dir.display(), cx.primary);

    let primary = cx.primary;
    let rustc = prepare_rustc(root, target, crate_types, cx);

    log!(5, "command={}", rustc);

    let _ = cx.config.shell().verbose(|shell| shell.status("Running", rustc.to_str()));

    proc() {
        if primary {
            rustc.exec().map_err(|err| human(err.to_str()))
        } else {
            rustc.exec_with_output().and(Ok(())).map_err(|err| {
                human(err.to_str())
            })
        }
    }
}

fn prepare_rustc(root: &Path, target: &Target, crate_types: Vec<&str>,
                 cx: &Context) -> ProcessBuilder {
    let mut args = Vec::new();

    build_base_args(&mut args, target, crate_types, cx);
    build_deps_args(&mut args, cx);

    util::process("rustc")
        .cwd(root.clone())
        .args(args.as_slice())
        .env("RUST_LOG", None) // rustc is way too noisy
}

fn build_base_args(into: &mut Args,
                   target: &Target,
                   crate_types: Vec<&str>,
                   cx: &Context)
{
    let metadata = target.get_metadata();

    // TODO: Handle errors in converting paths into args
    into.push(target.get_src_path().display().to_str());

    into.push("--crate-name".to_str());
    into.push(target.get_name().to_str());

    for crate_type in crate_types.iter() {
        into.push("--crate-type".to_str());
        into.push(crate_type.to_str());
    }

    let out = cx.dest.clone();
    let profile = target.get_profile();

    if profile.get_opt_level() != 0 {
        into.push("--opt-level".to_str());
        into.push(profile.get_opt_level().to_str());
    }

    if profile.get_debug() {
        into.push("-g".to_str());
    }

    if profile.is_test() {
        into.push("--test".to_str());
    }

    match metadata {
        Some(m) => {
            into.push("-C".to_str());
            into.push(format!("metadata={}", m.metadata));

            into.push("-C".to_str());
            into.push(format!("extra-filename=-{}", m.extra_filename));
        }
        None => {}
    }

    if target.is_lib() {
        into.push("--out-dir".to_str());
        into.push(out.display().to_str());
    } else {
        into.push("-o".to_str());
        into.push(out.join(target.get_name()).display().to_str());
    }
}

fn build_deps_args(dst: &mut Args, cx: &Context) {
    dst.push("-L".to_str());
    dst.push(cx.dest.display().to_str());
    dst.push("-L".to_str());
    dst.push(cx.deps_dir.display().to_str());
}

/// Execute all jobs necessary to build the dependency graph.
///
/// This function will spawn off `config.jobs()` workers to build all of the
/// necessary dependencies, in order. Freshness is propagated as far as possible
/// along each dependency chain.
fn execute(config: &mut Config,
           jobs: Vec<(&Package, (Freshness, Job))>) -> CargoResult<()> {
    let pool = TaskPool::new(config.jobs());
    let (tx, rx) = channel();
    let mut queue = DependencyQueue::new();
    for &(pkg, _) in jobs.iter() {
        queue.register(pkg);
    }
    for (pkg, (fresh, job)) in jobs.move_iter() {
        queue.enqueue(pkg, fresh, (pkg, job));
    }

    // Iteratively execute the dependency graph. Each turn of this loop will
    // schedule as much work as possible and then wait for one job to finish,
    // possibly scheduling more work afterwards.
    let mut active = 0i;
    while queue.len() > 0 {
        loop {
            match queue.dequeue() {
                Some((name, Fresh, (pkg, _))) => {
                    try!(config.shell().status("Fresh", pkg));
                    tx.send((name, Fresh, Ok(())));
                }
                Some((name, Dirty, (pkg, job))) => {
                    try!(config.shell().status("Compiling", pkg));
                    let my_tx = tx.clone();
                    pool.execute(proc() my_tx.send((name, Dirty, job())));
                }
                None => break,
            }
        }

        // Now that all possible work has been scheduled, wait for a piece of
        // work to finish. If any package fails to build then we stop scheduling
        // work as quickly as possibly.
        active -= 1;
        match rx.recv() {
            (name, fresh, Ok(())) => queue.finish(&name, fresh),
            (_, _, Err(e)) => {
                if active > 0 && config.jobs() > 1 {
                    try!(config.shell().say("Build failed, waiting for other \
                                             jobs to finish...", YELLOW));
                    for _ in rx.iter() {}
                }
                return Err(e)
            }
        }
    }

    log!(5, "rustc jobs completed");

    Ok(())
}
<commit_msg>Disable passing -g for now<commit_after>use std::hash::Hasher;
use std::hash::sip::SipHasher;
use std::io::{File, IoError};
use std::io;
use std::os::args;
use std::str;
use term::color::YELLOW;

use core::{Package, PackageSet, Target};
use util;
use util::{CargoResult, ChainError, ProcessBuilder, internal, human, CargoError};
use util::{Config, TaskPool, DependencyQueue, Fresh, Dirty, Freshness};

type Args = Vec<String>;

struct Context<'a, 'b> {
    dest: &'a Path,
    deps_dir: &'a Path,
    primary: bool,
    rustc_version: &'a str,
    config: &'b mut Config<'b>
}

type Job = proc():Send -> CargoResult<()>;

// This is a temporary assert that ensures the consistency of the arguments
// given the current limitations of Cargo. The long term fix is to have each
// Target know the absolute path to the build location.
fn uniq_target_dest<'a>(targets: &[&'a Target]) -> Option<&'a str> {
    let mut curr: Option<Option<&str>> = None;

    for t in targets.iter() {
        let dest = t.get_profile().get_dest();

        match curr {
            Some(curr) => assert!(curr == dest),
            None => curr = Some(dest)
        }
    }

    curr.unwrap()
}

pub fn compile_targets<'a>(env: &str, targets: &[&Target], pkg: &Package,
                           deps: &PackageSet,
                           config: &'a mut Config<'a>) -> CargoResult<()> {

    if targets.is_empty() {
        return Ok(());
    }

    debug!("compile_targets; targets={}; pkg={}; deps={}", targets, pkg, deps);

    let path_fragment = uniq_target_dest(targets);
    let target_dir = pkg.get_absolute_target_dir().join(path_fragment.unwrap_or(""));
    let deps_target_dir = target_dir.join("deps");

    let output = try!(util::process("rustc").arg("-v").exec_with_output());
    let rustc_version = str::from_utf8(output.output.as_slice()).unwrap();

    // First ensure that the destination directory exists
    debug!("creating target dir; path={}", target_dir.display());

    try!(mk_target(&target_dir).chain_error(||
        internal(format!("Couldn't create the target directory for {} at {}",
                 pkg.get_name(), target_dir.display()))));

    try!(mk_target(&deps_target_dir).chain_error(||
        internal(format!("Couldn't create the directory for dependencies for {} at {}",
                 pkg.get_name(), deps_target_dir.display()))));

    let mut cx = Context {
        dest: &deps_target_dir,
        deps_dir: &deps_target_dir,
        primary: false,
        rustc_version: rustc_version.as_slice(),
        config: config
    };

    // Build up a list of pending jobs, each of which represent compiling a
    // particular package. No actual work is executed as part of this, that's
    // all done later as part of the `execute` function which will run
    // everything in order with proper parallelism.
    let mut jobs = Vec::new();
    for dep in deps.iter() {
        // Only compile lib targets for dependencies
        let targets = dep.get_targets().iter().filter(|target| {
            target.is_lib() && target.get_profile().get_env() == env
        }).collect::<Vec<&Target>>();

        jobs.push((dep, try!(compile(targets.as_slice(), dep, &mut cx))));
    }

    cx.primary = true;
    cx.dest = &target_dir;
    jobs.push((pkg, try!(compile(targets, pkg, &mut cx))));

    // Now that we've figured out everything that we're going to do, do it!
    execute(cx.config, jobs)
}

fn compile(targets: &[&Target], pkg: &Package,
           cx: &mut Context) -> CargoResult<(Freshness, Job)> {
    debug!("compile_pkg; pkg={}; targets={}", pkg, pkg.get_targets());

    if targets.is_empty() {
        return Ok((Fresh, proc() Ok(())))
    }

    // First check to see if this package is fresh.
    //
    // Note that we're compiling things in topological order, so if nothing has
    // been built up to this point and we're fresh, then we can safely skip
    // recompilation. If anything has previously been rebuilt, it may have been
    // a dependency of ours, so just go ahead and rebuild ourselves.
    //
    // This is not quite accurate, we should only trigger forceful
    // recompilations for downstream dependencies of ourselves, not everyone
    // compiled afterwards.a
    //
    // TODO: Figure out how this works with targets
    let fingerprint_loc = cx.dest.join(format!(".{}.fingerprint",
                                               pkg.get_name()));

    let (is_fresh, fingerprint) = try!(is_fresh(pkg, &fingerprint_loc, cx,
                                                targets));

    let mut cmds = Vec::new();

    // TODO: Should this be on the target or the package?
    for build_cmd in pkg.get_manifest().get_build().iter() {
        cmds.push(compile_custom(pkg, build_cmd.as_slice(), cx));
    }

    // After the custom command has run, execute rustc for all targets of our
    // package.
    for &target in targets.iter() {
        cmds.push(rustc(&pkg.get_root(), target, cx));
    }

    cmds.push(proc() {
        // If this job runs, then everything has successfully compiled, so write
        // our new fingerprint to the relevant location to prevent
        // recompilations in the future.
        try!(File::create(&fingerprint_loc).write_str(fingerprint.as_slice()));
        Ok(())
    });

    // TODO: this job itself may internally be parallel, but we're hiding that
    //       currently. How to expose the parallelism among a single target?
    Ok((if is_fresh {Fresh} else {Dirty}, proc() {
        for cmd in cmds.move_iter() {
            try!(cmd());
        }
        Ok(())
    }))
}

fn is_fresh(dep: &Package, loc: &Path,
            cx: &mut Context, targets: &[&Target]) -> CargoResult<(bool, String)>
{
    let new_pkg_fingerprint = format!("{}{}", cx.rustc_version,
                                  try!(dep.get_fingerprint(cx.config)));

    let new_fingerprint = fingerprint(new_pkg_fingerprint, hash_targets(targets));

    let mut file = match File::open(loc) {
        Ok(file) => file,
        Err(..) => return Ok((false, new_fingerprint)),
    };

    let old_fingerprint = try!(file.read_to_str());

    log!(5, "old fingerprint: {}", old_fingerprint);
    log!(5, "new fingerprint: {}", new_fingerprint);

    Ok((old_fingerprint == new_fingerprint, new_fingerprint))
}

fn hash_targets(targets: &[&Target]) -> u64 {
    let hasher = SipHasher::new_with_keys(0,0);
    let targets = targets.iter().map(|t| (*t).clone()).collect::<Vec<Target>>();
    hasher.hash(&targets)
}

fn fingerprint(package: String, profiles: u64) -> String {
    let hasher = SipHasher::new_with_keys(0,0);
    util::to_hex(hasher.hash(&(package, profiles)))
}

fn mk_target(target: &Path) -> Result<(), IoError> {
    io::fs::mkdir_recursive(target, io::UserRWX)
}

fn compile_custom(pkg: &Package, cmd: &str,
                  cx: &Context) -> Job {
    // FIXME: this needs to be smarter about splitting
    let mut cmd = cmd.split(' ');
    let mut p = util::process(cmd.next().unwrap())
                     .cwd(pkg.get_root())
                     .env("OUT_DIR", Some(cx.dest.as_str().expect("non-UTF8 dest path")))
                     .env("DEPS_DIR", Some(cx.dest.join(cx.deps_dir)
                                             .as_str().expect("non-UTF8 deps path")));
    for arg in cmd {
        p = p.arg(arg);
    }
    proc() p.exec_with_output().map(|_| ()).map_err(|e| e.mark_human())
}

fn rustc(root: &Path, target: &Target, cx: &mut Context) -> Job {
    let crate_types = target.rustc_crate_types();

    log!(5, "root={}; target={}; crate_types={}; dest={}; deps={}; verbose={}",
         root.display(), target, crate_types, cx.dest.display(),
         cx.deps_dir.display(), cx.primary);

    let primary = cx.primary;
    let rustc = prepare_rustc(root, target, crate_types, cx);

    log!(5, "command={}", rustc);

    let _ = cx.config.shell().verbose(|shell| shell.status("Running", rustc.to_str()));

    proc() {
        if primary {
            rustc.exec().map_err(|err| human(err.to_str()))
        } else {
            rustc.exec_with_output().and(Ok(())).map_err(|err| {
                human(err.to_str())
            })
        }
    }
}

fn prepare_rustc(root: &Path, target: &Target, crate_types: Vec<&str>,
                 cx: &Context) -> ProcessBuilder {
    let mut args = Vec::new();

    build_base_args(&mut args, target, crate_types, cx);
    build_deps_args(&mut args, cx);

    util::process("rustc")
        .cwd(root.clone())
        .args(args.as_slice())
        .env("RUST_LOG", None) // rustc is way too noisy
}

fn build_base_args(into: &mut Args,
                   target: &Target,
                   crate_types: Vec<&str>,
                   cx: &Context)
{
    let metadata = target.get_metadata();

    // TODO: Handle errors in converting paths into args
    into.push(target.get_src_path().display().to_str());

    into.push("--crate-name".to_str());
    into.push(target.get_name().to_str());

    for crate_type in crate_types.iter() {
        into.push("--crate-type".to_str());
        into.push(crate_type.to_str());
    }

    let out = cx.dest.clone();
    let profile = target.get_profile();

    if profile.get_opt_level() != 0 {
        into.push("--opt-level".to_str());
        into.push(profile.get_opt_level().to_str());
    }

    // Right now -g is a little buggy, so we're not passing -g just yet
    // if profile.get_debug() {
    //     into.push("-g".to_str());
    // }

    if profile.is_test() {
        into.push("--test".to_str());
    }

    match metadata {
        Some(m) => {
            into.push("-C".to_str());
            into.push(format!("metadata={}", m.metadata));

            into.push("-C".to_str());
            into.push(format!("extra-filename=-{}", m.extra_filename));
        }
        None => {}
    }

    if target.is_lib() {
        into.push("--out-dir".to_str());
        into.push(out.display().to_str());
    } else {
        into.push("-o".to_str());
        into.push(out.join(target.get_name()).display().to_str());
    }
}

fn build_deps_args(dst: &mut Args, cx: &Context) {
    dst.push("-L".to_str());
    dst.push(cx.dest.display().to_str());
    dst.push("-L".to_str());
    dst.push(cx.deps_dir.display().to_str());
}

/// Execute all jobs necessary to build the dependency graph.
///
/// This function will spawn off `config.jobs()` workers to build all of the
/// necessary dependencies, in order. Freshness is propagated as far as possible
/// along each dependency chain.
fn execute(config: &mut Config,
           jobs: Vec<(&Package, (Freshness, Job))>) -> CargoResult<()> {
    let pool = TaskPool::new(config.jobs());
    let (tx, rx) = channel();
    let mut queue = DependencyQueue::new();
    for &(pkg, _) in jobs.iter() {
        queue.register(pkg);
    }
    for (pkg, (fresh, job)) in jobs.move_iter() {
        queue.enqueue(pkg, fresh, (pkg, job));
    }

    // Iteratively execute the dependency graph. Each turn of this loop will
    // schedule as much work as possible and then wait for one job to finish,
    // possibly scheduling more work afterwards.
    let mut active = 0i;
    while queue.len() > 0 {
        loop {
            match queue.dequeue() {
                Some((name, Fresh, (pkg, _))) => {
                    try!(config.shell().status("Fresh", pkg));
                    tx.send((name, Fresh, Ok(())));
                }
                Some((name, Dirty, (pkg, job))) => {
                    try!(config.shell().status("Compiling", pkg));
                    let my_tx = tx.clone();
                    pool.execute(proc() my_tx.send((name, Dirty, job())));
                }
                None => break,
            }
        }

        // Now that all possible work has been scheduled, wait for a piece of
        // work to finish. If any package fails to build then we stop scheduling
        // work as quickly as possibly.
        active -= 1;
        match rx.recv() {
            (name, fresh, Ok(())) => queue.finish(&name, fresh),
            (_, _, Err(e)) => {
                if active > 0 && config.jobs() > 1 {
                    try!(config.shell().say("Build failed, waiting for other \
                                             jobs to finish...", YELLOW));
                    for _ in rx.iter() {}
                }
                return Err(e)
            }
        }
    }

    log!(5, "rustc jobs completed");

    Ok(())
}
<|endoftext|>


<commit_before>//! Sparse mapping of entity references to larger value types.
//!
//! This module provides a `SparseMap` data structure which implements a sparse mapping from an
//! `EntityRef` key to a value type that may be on the larger side. This implementation is based on
//! the paper:
//!
//! > Briggs, Torczon, *An efficient representation for sparse sets*,
//!   ACM Letters on Programming Languages and Systems, Volume 2, Issue 1-4, March-Dec. 1993.
//!
//! A `SparseMap<K, V>` map provides:
//!
//! - Memory usage equivalent to `EntityMap<K, u32>` + `Vec<V>`, so much smaller than
//!   `EntityMap<K, V>` for sparse mappings of larger `V` types.
//! - Constant time lookup, slightly slower than `EntityMap`.
//! - A very fast, constant time `clear()` operation.
//! - Fast insert and erase operations.
//! - Stable iteration that is as fast as a `Vec<V>`.
//!
//! # Compared to `EntityMap`
//!
//! When should we use a `SparseMap` instead of a secondary `EntityMap`? First of all, `SparseMap`
//! does not provide the functionality of a primary `EntityMap` which can allocate and assign
//! entity references to objects as they are pushed onto the map. It is only the secondary
//! entity maps that can be replaced with a `SparseMap`.
//!
//! - A secondary entity map requires its values to implement `Default`, and it is a bit loose
//!   about creating new mappings to the default value. It doesn't distinguish clearly between an
//!   unmapped key and one that maps to the default value. `SparseMap` does not require `Default`
//!   values, and it tracks accurately if a key has been mapped or not.
//! - Iterating over the contents of an `EntityMap` is linear in the size of the *key space*, while
//!   iterating over a `SparseMap` is linear in the number of elements in the mapping. This is an
//!   advantage precisely when the mapping is sparse.
//! - `SparseMap::clear()` is constant time and super-fast. `EntityMap::clear()` is linear in the
//!   size of the key space. (Or, rather the required `resize()` call following the `clear()` is).
//! - `SparseMap` requires the values to implement `SparseMapValue<K>` which means that they must
//!   contain their own key.

use entity_map::{EntityRef, EntityMap};
use std::mem;
use std::slice;
use std::u32;

/// Trait for extracting keys from values stored in a `SparseMap`.
///
/// All values stored in a `SparseMap` must keep track of their own key in the map and implement
/// this trait to provide access to the key.
pub trait SparseMapValue<K> {
    /// Get the key of this sparse map value. This key is not allowed to change while the value
    /// is a member of the map.
    fn key(&self) -> K;
}

/// A sparse mapping of entity references.
pub struct SparseMap<K, V>
    where K: EntityRef,
          V: SparseMapValue<K>
{
    sparse: EntityMap<K, u32>,
    dense: Vec<V>,
}

impl<K, V> SparseMap<K, V>
    where K: EntityRef,
          V: SparseMapValue<K>
{
    /// Create a new empty mapping.
    pub fn new() -> Self {
        SparseMap {
            sparse: EntityMap::new(),
            dense: Vec::new(),
        }
    }

    /// Returns the number of elements in the map.
    pub fn len(&self) -> usize {
        self.dense.len()
    }

    /// Returns true is the map contains no elements.
    pub fn is_empty(&self) -> bool {
        self.dense.is_empty()
    }

    /// Remove all elements from the mapping.
    pub fn clear(&mut self) {
        self.dense.clear();
    }

    /// Returns a reference to the value corresponding to the key.
    pub fn get(&self, key: K) -> Option<&V> {
        if let Some(idx) = self.sparse.get(key).cloned() {
            if let Some(entry) = self.dense.get(idx as usize) {
                if entry.key() == key {
                    return Some(entry);
                }
            }
        }
        None
    }

    /// Returns a mutable reference to the value corresponding to the key.
    ///
    /// Note that the returned value must not be mutated in a way that would change its key. This
    /// would invalidate the sparse set data structure.
    pub fn get_mut(&mut self, key: K) -> Option<&mut V> {
        if let Some(idx) = self.sparse.get(key).cloned() {
            if let Some(entry) = self.dense.get_mut(idx as usize) {
                if entry.key() == key {
                    return Some(entry);
                }
            }
        }
        None
    }

    /// Return the index into `dense` of the value corresponding to `key`.
    fn index(&self, key: K) -> Option<usize> {
        if let Some(idx) = self.sparse.get(key).cloned() {
            let idx = idx as usize;
            if let Some(entry) = self.dense.get(idx) {
                if entry.key() == key {
                    return Some(idx);
                }
            }
        }
        None
    }

    /// Insert a value into the map.
    ///
    /// If the map did not have this key present, `None` is returned.
    ///
    /// If the map did have this key present, the value is updated, and the old value is returned.
    ///
    /// It is not necessary to provide a key since the value knows its own key already.
    pub fn insert(&mut self, value: V) -> Option<V> {
        let key = value.key();

        // Replace the existing entry for `key` if there is one.
        if let Some(entry) = self.get_mut(key) {
            return Some(mem::replace(entry, value));
        }

        // There was no previous entry for `key`. Add it to the end of `dense`.
        let idx = self.dense.len();
        assert!(idx <= u32::MAX as usize, "SparseMap overflow");
        self.dense.push(value);
        *self.sparse.ensure(key) = idx as u32;
        None
    }

    /// Remove a value from the map and return it.
    pub fn remove(&mut self, key: K) -> Option<V> {
        if let Some(idx) = self.index(key) {
            let back = self.dense.pop().unwrap();

            // Are we popping the back of `dense`?
            if idx == self.dense.len() {
                return Some(back);
            }

            // We're removing an element from the middle of `dense`.
            // Replace the element at `idx` with the back of `dense`.
            // Repair `sparse` first.
            self.sparse[back.key()] = idx as u32;
            return Some(mem::replace(&mut self.dense[idx], back));
        }

        // Nothing to remove.
        None
    }

    /// Get an iterator over the values in the map.
    ///
    /// The iteration order is entirely determined by the preceding sequence of `insert` and
    /// `remove` operations. In particular, if no elements were removed, this is the insertion
    /// order.
    pub fn values(&self) -> slice::Iter<V> {
        self.dense.iter()
    }
}

/// Iterating over the elements of a set.
impl<'a, K, V> IntoIterator for &'a SparseMap<K, V>
    where K: EntityRef,
          V: SparseMapValue<K>
{
    type Item = &'a V;
    type IntoIter = slice::Iter<'a, V>;

    fn into_iter(self) -> Self::IntoIter {
        self.values()
    }
}

/// Any `EntityRef` can be used as a sparse map value representing itself.
impl<T> SparseMapValue<T> for T
    where T: EntityRef
{
    fn key(&self) -> T {
        *self
    }
}

/// A sparse set of entity references.
///
/// Any type that implements `EntityRef` can be used as a sparse set value too.
pub type SparseSet<T> = SparseMap<T, T>;

#[cfg(test)]
mod tests {
    use super::*;
    use entity_map::EntityRef;
    use ir::Inst;

    // Mock key-value object for testing.
    #[derive(PartialEq, Eq, Debug)]
    struct Obj(Inst, &'static str);

    impl SparseMapValue<Inst> for Obj {
        fn key(&self) -> Inst {
            self.0
        }
    }

    #[test]
    fn empty_immutable_map() {
        let i1 = Inst::new(1);
        let map: SparseMap<Inst, Obj> = SparseMap::new();

        assert!(map.is_empty());
        assert_eq!(map.len(), 0);
        assert_eq!(map.get(i1), None);
        assert_eq!(map.values().count(), 0);
    }

    #[test]
    fn single_entry() {
        let i0 = Inst::new(0);
        let i1 = Inst::new(1);
        let i2 = Inst::new(2);
        let mut map = SparseMap::new();

        assert!(map.is_empty());
        assert_eq!(map.len(), 0);
        assert_eq!(map.get(i1), None);
        assert_eq!(map.get_mut(i1), None);
        assert_eq!(map.remove(i1), None);

        assert_eq!(map.insert(Obj(i1, "hi")), None);
        assert!(!map.is_empty());
        assert_eq!(map.len(), 1);
        assert_eq!(map.get(i0), None);
        assert_eq!(map.get(i1), Some(&Obj(i1, "hi")));
        assert_eq!(map.get(i2), None);
        assert_eq!(map.get_mut(i0), None);
        assert_eq!(map.get_mut(i1), Some(&mut Obj(i1, "hi")));
        assert_eq!(map.get_mut(i2), None);

        assert_eq!(map.remove(i0), None);
        assert_eq!(map.remove(i2), None);
        assert_eq!(map.remove(i1), Some(Obj(i1, "hi")));
        assert_eq!(map.len(), 0);
        assert_eq!(map.get(i1), None);
        assert_eq!(map.get_mut(i1), None);
        assert_eq!(map.remove(i0), None);
        assert_eq!(map.remove(i1), None);
        assert_eq!(map.remove(i2), None);
    }

    #[test]
    fn multiple_entries() {
        let i0 = Inst::new(0);
        let i1 = Inst::new(1);
        let i2 = Inst::new(2);
        let i3 = Inst::new(3);
        let mut map = SparseMap::new();

        assert_eq!(map.insert(Obj(i2, "foo")), None);
        assert_eq!(map.insert(Obj(i1, "bar")), None);
        assert_eq!(map.insert(Obj(i0, "baz")), None);

        // Iteration order = insertion order when nothing has been removed yet.
        assert_eq!(map.values().map(|obj| obj.1).collect::<Vec<_>>(),
                   ["foo", "bar", "baz"]);

        assert_eq!(map.len(), 3);
        assert_eq!(map.get(i0), Some(&Obj(i0, "baz")));
        assert_eq!(map.get(i1), Some(&Obj(i1, "bar")));
        assert_eq!(map.get(i2), Some(&Obj(i2, "foo")));
        assert_eq!(map.get(i3), None);

        // Remove front object, causing back to be swapped down.
        assert_eq!(map.remove(i1), Some(Obj(i1, "bar")));
        assert_eq!(map.len(), 2);
        assert_eq!(map.get(i0), Some(&Obj(i0, "baz")));
        assert_eq!(map.get(i1), None);
        assert_eq!(map.get(i2), Some(&Obj(i2, "foo")));
        assert_eq!(map.get(i3), None);

        // Reinsert something at a previously used key.
        assert_eq!(map.insert(Obj(i1, "barbar")), None);
        assert_eq!(map.len(), 3);
        assert_eq!(map.get(i0), Some(&Obj(i0, "baz")));
        assert_eq!(map.get(i1), Some(&Obj(i1, "barbar")));
        assert_eq!(map.get(i2), Some(&Obj(i2, "foo")));
        assert_eq!(map.get(i3), None);

        // Replace an entry.
        assert_eq!(map.insert(Obj(i0, "bazbaz")), Some(Obj(i0, "baz")));
        assert_eq!(map.len(), 3);
        assert_eq!(map.get(i0), Some(&Obj(i0, "bazbaz")));
        assert_eq!(map.get(i1), Some(&Obj(i1, "barbar")));
        assert_eq!(map.get(i2), Some(&Obj(i2, "foo")));
        assert_eq!(map.get(i3), None);

        // Check the reference `IntoIter` impl.
        let mut v = Vec::new();
        for i in &map {
            v.push(i.1);
        }
        assert_eq!(v.len(), map.len());
    }

    #[test]
    fn entity_set() {
        let i0 = Inst::new(0);
        let i1 = Inst::new(1);
        let mut set = SparseSet::new();

        assert_eq!(set.insert(i0), None);
        assert_eq!(set.insert(i0), Some(i0));
        assert_eq!(set.insert(i1), None);
        assert_eq!(set.get(i0), Some(&i0));
        assert_eq!(set.get(i1), Some(&i1));
    }
}
<commit_msg>Add a contains_key method to SparseMap.<commit_after>//! Sparse mapping of entity references to larger value types.
//!
//! This module provides a `SparseMap` data structure which implements a sparse mapping from an
//! `EntityRef` key to a value type that may be on the larger side. This implementation is based on
//! the paper:
//!
//! > Briggs, Torczon, *An efficient representation for sparse sets*,
//!   ACM Letters on Programming Languages and Systems, Volume 2, Issue 1-4, March-Dec. 1993.
//!
//! A `SparseMap<K, V>` map provides:
//!
//! - Memory usage equivalent to `EntityMap<K, u32>` + `Vec<V>`, so much smaller than
//!   `EntityMap<K, V>` for sparse mappings of larger `V` types.
//! - Constant time lookup, slightly slower than `EntityMap`.
//! - A very fast, constant time `clear()` operation.
//! - Fast insert and erase operations.
//! - Stable iteration that is as fast as a `Vec<V>`.
//!
//! # Compared to `EntityMap`
//!
//! When should we use a `SparseMap` instead of a secondary `EntityMap`? First of all, `SparseMap`
//! does not provide the functionality of a primary `EntityMap` which can allocate and assign
//! entity references to objects as they are pushed onto the map. It is only the secondary
//! entity maps that can be replaced with a `SparseMap`.
//!
//! - A secondary entity map requires its values to implement `Default`, and it is a bit loose
//!   about creating new mappings to the default value. It doesn't distinguish clearly between an
//!   unmapped key and one that maps to the default value. `SparseMap` does not require `Default`
//!   values, and it tracks accurately if a key has been mapped or not.
//! - Iterating over the contents of an `EntityMap` is linear in the size of the *key space*, while
//!   iterating over a `SparseMap` is linear in the number of elements in the mapping. This is an
//!   advantage precisely when the mapping is sparse.
//! - `SparseMap::clear()` is constant time and super-fast. `EntityMap::clear()` is linear in the
//!   size of the key space. (Or, rather the required `resize()` call following the `clear()` is).
//! - `SparseMap` requires the values to implement `SparseMapValue<K>` which means that they must
//!   contain their own key.

use entity_map::{EntityRef, EntityMap};
use std::mem;
use std::slice;
use std::u32;

/// Trait for extracting keys from values stored in a `SparseMap`.
///
/// All values stored in a `SparseMap` must keep track of their own key in the map and implement
/// this trait to provide access to the key.
pub trait SparseMapValue<K> {
    /// Get the key of this sparse map value. This key is not allowed to change while the value
    /// is a member of the map.
    fn key(&self) -> K;
}

/// A sparse mapping of entity references.
pub struct SparseMap<K, V>
    where K: EntityRef,
          V: SparseMapValue<K>
{
    sparse: EntityMap<K, u32>,
    dense: Vec<V>,
}

impl<K, V> SparseMap<K, V>
    where K: EntityRef,
          V: SparseMapValue<K>
{
    /// Create a new empty mapping.
    pub fn new() -> Self {
        SparseMap {
            sparse: EntityMap::new(),
            dense: Vec::new(),
        }
    }

    /// Returns the number of elements in the map.
    pub fn len(&self) -> usize {
        self.dense.len()
    }

    /// Returns true is the map contains no elements.
    pub fn is_empty(&self) -> bool {
        self.dense.is_empty()
    }

    /// Remove all elements from the mapping.
    pub fn clear(&mut self) {
        self.dense.clear();
    }

    /// Returns a reference to the value corresponding to the key.
    pub fn get(&self, key: K) -> Option<&V> {
        if let Some(idx) = self.sparse.get(key).cloned() {
            if let Some(entry) = self.dense.get(idx as usize) {
                if entry.key() == key {
                    return Some(entry);
                }
            }
        }
        None
    }

    /// Returns a mutable reference to the value corresponding to the key.
    ///
    /// Note that the returned value must not be mutated in a way that would change its key. This
    /// would invalidate the sparse set data structure.
    pub fn get_mut(&mut self, key: K) -> Option<&mut V> {
        if let Some(idx) = self.sparse.get(key).cloned() {
            if let Some(entry) = self.dense.get_mut(idx as usize) {
                if entry.key() == key {
                    return Some(entry);
                }
            }
        }
        None
    }

    /// Return the index into `dense` of the value corresponding to `key`.
    fn index(&self, key: K) -> Option<usize> {
        if let Some(idx) = self.sparse.get(key).cloned() {
            let idx = idx as usize;
            if let Some(entry) = self.dense.get(idx) {
                if entry.key() == key {
                    return Some(idx);
                }
            }
        }
        None
    }

    /// Return `true` if the map contains a value corresponding to `key`.
    pub fn contains_key(&self, key: K) -> bool {
        self.get(key).is_some()
    }

    /// Insert a value into the map.
    ///
    /// If the map did not have this key present, `None` is returned.
    ///
    /// If the map did have this key present, the value is updated, and the old value is returned.
    ///
    /// It is not necessary to provide a key since the value knows its own key already.
    pub fn insert(&mut self, value: V) -> Option<V> {
        let key = value.key();

        // Replace the existing entry for `key` if there is one.
        if let Some(entry) = self.get_mut(key) {
            return Some(mem::replace(entry, value));
        }

        // There was no previous entry for `key`. Add it to the end of `dense`.
        let idx = self.dense.len();
        assert!(idx <= u32::MAX as usize, "SparseMap overflow");
        self.dense.push(value);
        *self.sparse.ensure(key) = idx as u32;
        None
    }

    /// Remove a value from the map and return it.
    pub fn remove(&mut self, key: K) -> Option<V> {
        if let Some(idx) = self.index(key) {
            let back = self.dense.pop().unwrap();

            // Are we popping the back of `dense`?
            if idx == self.dense.len() {
                return Some(back);
            }

            // We're removing an element from the middle of `dense`.
            // Replace the element at `idx` with the back of `dense`.
            // Repair `sparse` first.
            self.sparse[back.key()] = idx as u32;
            return Some(mem::replace(&mut self.dense[idx], back));
        }

        // Nothing to remove.
        None
    }

    /// Get an iterator over the values in the map.
    ///
    /// The iteration order is entirely determined by the preceding sequence of `insert` and
    /// `remove` operations. In particular, if no elements were removed, this is the insertion
    /// order.
    pub fn values(&self) -> slice::Iter<V> {
        self.dense.iter()
    }
}

/// Iterating over the elements of a set.
impl<'a, K, V> IntoIterator for &'a SparseMap<K, V>
    where K: EntityRef,
          V: SparseMapValue<K>
{
    type Item = &'a V;
    type IntoIter = slice::Iter<'a, V>;

    fn into_iter(self) -> Self::IntoIter {
        self.values()
    }
}

/// Any `EntityRef` can be used as a sparse map value representing itself.
impl<T> SparseMapValue<T> for T
    where T: EntityRef
{
    fn key(&self) -> T {
        *self
    }
}

/// A sparse set of entity references.
///
/// Any type that implements `EntityRef` can be used as a sparse set value too.
pub type SparseSet<T> = SparseMap<T, T>;

#[cfg(test)]
mod tests {
    use super::*;
    use entity_map::EntityRef;
    use ir::Inst;

    // Mock key-value object for testing.
    #[derive(PartialEq, Eq, Debug)]
    struct Obj(Inst, &'static str);

    impl SparseMapValue<Inst> for Obj {
        fn key(&self) -> Inst {
            self.0
        }
    }

    #[test]
    fn empty_immutable_map() {
        let i1 = Inst::new(1);
        let map: SparseMap<Inst, Obj> = SparseMap::new();

        assert!(map.is_empty());
        assert_eq!(map.len(), 0);
        assert_eq!(map.get(i1), None);
        assert_eq!(map.values().count(), 0);
    }

    #[test]
    fn single_entry() {
        let i0 = Inst::new(0);
        let i1 = Inst::new(1);
        let i2 = Inst::new(2);
        let mut map = SparseMap::new();

        assert!(map.is_empty());
        assert_eq!(map.len(), 0);
        assert_eq!(map.get(i1), None);
        assert_eq!(map.get_mut(i1), None);
        assert_eq!(map.remove(i1), None);

        assert_eq!(map.insert(Obj(i1, "hi")), None);
        assert!(!map.is_empty());
        assert_eq!(map.len(), 1);
        assert_eq!(map.get(i0), None);
        assert_eq!(map.get(i1), Some(&Obj(i1, "hi")));
        assert_eq!(map.get(i2), None);
        assert_eq!(map.get_mut(i0), None);
        assert_eq!(map.get_mut(i1), Some(&mut Obj(i1, "hi")));
        assert_eq!(map.get_mut(i2), None);

        assert_eq!(map.remove(i0), None);
        assert_eq!(map.remove(i2), None);
        assert_eq!(map.remove(i1), Some(Obj(i1, "hi")));
        assert_eq!(map.len(), 0);
        assert_eq!(map.get(i1), None);
        assert_eq!(map.get_mut(i1), None);
        assert_eq!(map.remove(i0), None);
        assert_eq!(map.remove(i1), None);
        assert_eq!(map.remove(i2), None);
    }

    #[test]
    fn multiple_entries() {
        let i0 = Inst::new(0);
        let i1 = Inst::new(1);
        let i2 = Inst::new(2);
        let i3 = Inst::new(3);
        let mut map = SparseMap::new();

        assert_eq!(map.insert(Obj(i2, "foo")), None);
        assert_eq!(map.insert(Obj(i1, "bar")), None);
        assert_eq!(map.insert(Obj(i0, "baz")), None);

        // Iteration order = insertion order when nothing has been removed yet.
        assert_eq!(map.values().map(|obj| obj.1).collect::<Vec<_>>(),
                   ["foo", "bar", "baz"]);

        assert_eq!(map.len(), 3);
        assert_eq!(map.get(i0), Some(&Obj(i0, "baz")));
        assert_eq!(map.get(i1), Some(&Obj(i1, "bar")));
        assert_eq!(map.get(i2), Some(&Obj(i2, "foo")));
        assert_eq!(map.get(i3), None);

        // Remove front object, causing back to be swapped down.
        assert_eq!(map.remove(i1), Some(Obj(i1, "bar")));
        assert_eq!(map.len(), 2);
        assert_eq!(map.get(i0), Some(&Obj(i0, "baz")));
        assert_eq!(map.get(i1), None);
        assert_eq!(map.get(i2), Some(&Obj(i2, "foo")));
        assert_eq!(map.get(i3), None);

        // Reinsert something at a previously used key.
        assert_eq!(map.insert(Obj(i1, "barbar")), None);
        assert_eq!(map.len(), 3);
        assert_eq!(map.get(i0), Some(&Obj(i0, "baz")));
        assert_eq!(map.get(i1), Some(&Obj(i1, "barbar")));
        assert_eq!(map.get(i2), Some(&Obj(i2, "foo")));
        assert_eq!(map.get(i3), None);

        // Replace an entry.
        assert_eq!(map.insert(Obj(i0, "bazbaz")), Some(Obj(i0, "baz")));
        assert_eq!(map.len(), 3);
        assert_eq!(map.get(i0), Some(&Obj(i0, "bazbaz")));
        assert_eq!(map.get(i1), Some(&Obj(i1, "barbar")));
        assert_eq!(map.get(i2), Some(&Obj(i2, "foo")));
        assert_eq!(map.get(i3), None);

        // Check the reference `IntoIter` impl.
        let mut v = Vec::new();
        for i in &map {
            v.push(i.1);
        }
        assert_eq!(v.len(), map.len());
    }

    #[test]
    fn entity_set() {
        let i0 = Inst::new(0);
        let i1 = Inst::new(1);
        let mut set = SparseSet::new();

        assert_eq!(set.insert(i0), None);
        assert_eq!(set.insert(i0), Some(i0));
        assert_eq!(set.insert(i1), None);
        assert_eq!(set.get(i0), Some(&i0));
        assert_eq!(set.get(i1), Some(&i1));
    }
}
<|endoftext|>


<commit_before>pub mod parse;

use rustc_serialize::json::Json;

use super::analysis::Analyzer;
use super::{Document, Value};

#[derive(Debug, PartialEq)]
pub enum FilterParseError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownFilterType(String),
    NoFilter,
}

#[derive(Debug, PartialEq)]
pub enum Filter {
    Term {
        field: String,
        value: Value,
    },
    Terms {
        field: String,
        values: Vec<Value>,
    },
    Prefix {
        field: String,
        value: String,
    },
    Missing {
        field: String,
    },
    And {
        children: Vec<Filter>,
    },
    Or {
        children: Vec<Filter>,
    },
    Not {
        child: Box<Filter>,
    },
}

#[derive(Debug, PartialEq)]
pub enum QueryParseError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownQueryType(String),
    NoQuery,
    FilteredNoFilter,
    FilteredNoQuery,
    FilterParseError(FilterParseError),
    MissingQueryString,
    MultiMatchMissingFields,
    InvalidQueryOperator,
    InvalidQueryBoost,
    InvalidInteger,
}

#[derive(Debug, PartialEq)]
pub enum QueryOperator {
    Or,
    And,
}

impl Default for QueryOperator {
    fn default() -> QueryOperator {
        QueryOperator::Or
    }
}

#[derive(Debug, PartialEq)]
pub enum Query {
    MatchAll {
        boost: f64,
    },
    Match {
        fields: Vec<String>,
        query: String,
        operator: QueryOperator,
        boost: f64,
    },
    Bool {
        must: Vec<Query>,
        must_not: Vec<Query>,
        should: Vec<Query>,
        filter: Vec<Filter>,
        minimum_should_match: i32,
        boost: f64,
    },
}

impl Filter {
    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Filter::Term{ref field, ref value} => {
                if let Some(field_value) = doc.fields.get(field) {
                    return field_value == value;
                }

                false
            }
            Filter::Terms{ref field, ref values} => {
                if let Some(field_value) = doc.fields.get(field) {
                    for value in values.iter() {
                        if field_value == value {
                            return true;
                        }
                    }
                }

                false
            }
            Filter::Prefix{ref field, ref value} => {
                if let Some(field_value) = doc.fields.get(field) {
                    if let Value::String(ref field_value) = *field_value {
                        return field_value.starts_with(value);
                    }
                }

                false
            }
            Filter::Missing{ref field} => {
                match doc.fields.get(field) {
                    Some(&Value::Null) => true,
                    None => true,
                    _ => false,
                }
            }
            Filter::And{ref children} => {
                for child in children.iter() {
                    if !child.matches(doc) {
                        return false;
                    }
                }

                true
            }
            Filter::Or{ref children} => {
                for child in children.iter() {
                    if child.matches(doc) {
                        return true;
                    }
                }

                false
            }
            Filter::Not{ref child} => !child.matches(doc),
        }
    }
}

impl Query {
    pub fn rank(&self, doc: &Document) -> Option<f64> {
        match *self {
            Query::MatchAll{boost} => Some(boost),
            Query::Match{ref fields, ref query, ref operator, boost} => {
                let query = Analyzer::Standard.run(query.clone());
                let mut matches = 0;

                for token in query.iter() {
                    let mut token_matched = false;

                    for field in fields.iter() {
                        if let Some(&Value::TSVector(ref field_value)) = doc.fields.get(field) {
                            for field_token in field_value.iter() {
                                if token == field_token {
                                    matches += 1;
                                    token_matched = true;
                                }
                            }
                        }
                    }

                    // All tokens must match for queries with and operator
                    if operator == &QueryOperator::And && !token_matched {
                        return None;
                    }
                }

                if matches > 0 {
                    Some(matches as f64)
                } else {
                    None
                }
            }
            Query::Bool{ref must, ref must_not, ref should, ref filter, minimum_should_match, boost} => {
                let mut total_score: f64 = 0.0;
                let mut total_matched: i32 = 0;

                // Must not
                for query in must_not {
                    if query.matches(doc) {
                        return None;
                    }
                }

                // Filter
                for filter in filter {
                    if !filter.matches(doc) {
                        return None;
                    }
                }

                // Must
                for query in must {
                    match query.rank(doc) {
                        Some(score) => {
                            total_matched += 1;
                            total_score += score;
                        }
                        None => return None,
                    }
                }

                // Should
                let mut should_matched: i32 = 0;
                for query in should {
                    if let Some(score) = query.rank(doc) {
                        should_matched += 1;
                        total_matched += 1;
                        total_score += score;
                    }
                }

                if should_matched < minimum_should_match {
                    return None;
                }

                // Return average score of matched queries
                Some((total_score * boost) / total_matched as f64)
            }
        }
    }

    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Query::MatchAll{ref boost} => true,
            Query::Match{ref fields, ref query, ref operator, ref boost} => {
                for field in fields.iter() {
                    if let Some(&Value::String(ref field_value)) = doc.fields.get(field) {
                        let mut field_value = field_value.to_lowercase();
                        let mut query = query.to_lowercase();

                        if field_value.contains(&query) {
                            return true;
                        }
                    }
                }

                false
            }
            Query::Bool{ref must, ref must_not, ref should, ref filter, minimum_should_match, boost} => {
                // Must not
                for query in must_not {
                    if query.matches(doc) {
                        return false;
                    }
                }

                // Filter
                for filter in filter {
                    if !filter.matches(doc) {
                        return false;
                    }
                }

                // Must
                for query in must {
                    if !query.matches(doc) {
                        return false;
                    }
                }

                // Should
                if minimum_should_match > 0 {
                    let mut should_matched: i32 = 0;
                    for query in should {
                        if query.matches(doc) {
                            should_matched += 1;

                            if should_matched >= minimum_should_match {
                                return true;
                            }
                        }
                    }

                    return false;
                }

                return true;
            }
        }
    }
}
<commit_msg>Fixed score calculation of bool query<commit_after>pub mod parse;

use rustc_serialize::json::Json;

use super::analysis::Analyzer;
use super::{Document, Value};

#[derive(Debug, PartialEq)]
pub enum FilterParseError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownFilterType(String),
    NoFilter,
}

#[derive(Debug, PartialEq)]
pub enum Filter {
    Term {
        field: String,
        value: Value,
    },
    Terms {
        field: String,
        values: Vec<Value>,
    },
    Prefix {
        field: String,
        value: String,
    },
    Missing {
        field: String,
    },
    And {
        children: Vec<Filter>,
    },
    Or {
        children: Vec<Filter>,
    },
    Not {
        child: Box<Filter>,
    },
}

#[derive(Debug, PartialEq)]
pub enum QueryParseError {
    ExpectedObject,
    ExpectedString,
    ExpectedArray,
    UnknownQueryType(String),
    NoQuery,
    FilteredNoFilter,
    FilteredNoQuery,
    FilterParseError(FilterParseError),
    MissingQueryString,
    MultiMatchMissingFields,
    InvalidQueryOperator,
    InvalidQueryBoost,
    InvalidInteger,
}

#[derive(Debug, PartialEq)]
pub enum QueryOperator {
    Or,
    And,
}

impl Default for QueryOperator {
    fn default() -> QueryOperator {
        QueryOperator::Or
    }
}

#[derive(Debug, PartialEq)]
pub enum Query {
    MatchAll {
        boost: f64,
    },
    Match {
        fields: Vec<String>,
        query: String,
        operator: QueryOperator,
        boost: f64,
    },
    Bool {
        must: Vec<Query>,
        must_not: Vec<Query>,
        should: Vec<Query>,
        filter: Vec<Filter>,
        minimum_should_match: i32,
        boost: f64,
    },
}

impl Filter {
    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Filter::Term{ref field, ref value} => {
                if let Some(field_value) = doc.fields.get(field) {
                    return field_value == value;
                }

                false
            }
            Filter::Terms{ref field, ref values} => {
                if let Some(field_value) = doc.fields.get(field) {
                    for value in values.iter() {
                        if field_value == value {
                            return true;
                        }
                    }
                }

                false
            }
            Filter::Prefix{ref field, ref value} => {
                if let Some(field_value) = doc.fields.get(field) {
                    if let Value::String(ref field_value) = *field_value {
                        return field_value.starts_with(value);
                    }
                }

                false
            }
            Filter::Missing{ref field} => {
                match doc.fields.get(field) {
                    Some(&Value::Null) => true,
                    None => true,
                    _ => false,
                }
            }
            Filter::And{ref children} => {
                for child in children.iter() {
                    if !child.matches(doc) {
                        return false;
                    }
                }

                true
            }
            Filter::Or{ref children} => {
                for child in children.iter() {
                    if child.matches(doc) {
                        return true;
                    }
                }

                false
            }
            Filter::Not{ref child} => !child.matches(doc),
        }
    }
}

impl Query {
    pub fn rank(&self, doc: &Document) -> Option<f64> {
        match *self {
            Query::MatchAll{boost} => Some(boost),
            Query::Match{ref fields, ref query, ref operator, boost} => {
                let query = Analyzer::Standard.run(query.clone());
                let mut matches = 0;

                for token in query.iter() {
                    let mut token_matched = false;

                    for field in fields.iter() {
                        if let Some(&Value::TSVector(ref field_value)) = doc.fields.get(field) {
                            for field_token in field_value.iter() {
                                if token == field_token {
                                    matches += 1;
                                    token_matched = true;
                                }
                            }
                        }
                    }

                    // All tokens must match for queries with and operator
                    if operator == &QueryOperator::And && !token_matched {
                        return None;
                    }
                }

                if matches > 0 {
                    Some(matches as f64)
                } else {
                    None
                }
            }
            Query::Bool{ref must, ref must_not, ref should, ref filter, minimum_should_match, boost} => {
                let mut total_score: f64 = 0.0;

                // Must not
                for query in must_not {
                    if query.matches(doc) {
                        return None;
                    }
                }

                // Filter
                for filter in filter {
                    if !filter.matches(doc) {
                        return None;
                    }
                }

                // Must
                for query in must {
                    match query.rank(doc) {
                        Some(score) => {
                            total_score += score;
                        }
                        None => return None,
                    }
                }

                // Should
                let mut should_matched: i32 = 0;
                for query in should {
                    if let Some(score) = query.rank(doc) {
                        should_matched += 1;
                        total_score += score;
                    }
                }

                if should_matched < minimum_should_match {
                    return None;
                }

                // Return average score of matched queries
                Some((total_score * boost) / (must.len() + should.len()) as f64)
            }
        }
    }

    pub fn matches(&self, doc: &Document) -> bool {
        match *self {
            Query::MatchAll{ref boost} => true,
            Query::Match{ref fields, ref query, ref operator, ref boost} => {
                for field in fields.iter() {
                    if let Some(&Value::String(ref field_value)) = doc.fields.get(field) {
                        let mut field_value = field_value.to_lowercase();
                        let mut query = query.to_lowercase();

                        if field_value.contains(&query) {
                            return true;
                        }
                    }
                }

                false
            }
            Query::Bool{ref must, ref must_not, ref should, ref filter, minimum_should_match, boost} => {
                // Must not
                for query in must_not {
                    if query.matches(doc) {
                        return false;
                    }
                }

                // Filter
                for filter in filter {
                    if !filter.matches(doc) {
                        return false;
                    }
                }

                // Must
                for query in must {
                    if !query.matches(doc) {
                        return false;
                    }
                }

                // Should
                if minimum_should_match > 0 {
                    let mut should_matched: i32 = 0;
                    for query in should {
                        if query.matches(doc) {
                            should_matched += 1;

                            if should_matched >= minimum_should_match {
                                return true;
                            }
                        }
                    }

                    return false;
                }

                return true;
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Types for the *m.room.name* event.

use std::time::SystemTime;

use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

use crate::{EventType, InvalidInput, TryFromRaw, UnsignedData};

/// A human-friendly room name designed to be displayed to the end-user.
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename = "m.room.name", tag = "type")]
pub struct NameEvent {
    /// The event's content.
    pub content: NameEventContent,

    /// The unique identifier for the event.
    pub event_id: EventId,

    /// Time on originating homeserver when this event was sent.
    #[serde(with = "ruma_serde::time::ms_since_unix_epoch")]
    pub origin_server_ts: SystemTime,

    /// The previous content for this state key, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prev_content: Option<NameEventContent>,

    /// The unique identifier for the room associated with this event.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub room_id: Option<RoomId>,

    /// The unique identifier for the user who sent this event.
    pub sender: UserId,

    /// A key that determines which piece of room state the event represents.
    pub state_key: String,

    /// Additional key-value pairs not signed by the homeserver.
    #[serde(skip_serializing_if = "ruma_serde::is_default")]
    pub unsigned: UnsignedData,
}

/// The payload for `NameEvent`.
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct NameEventContent {
    /// The name of the room. This MUST NOT exceed 255 bytes.
    pub(crate) name: Option<String>,
}

impl TryFromRaw for NameEvent {
    type Raw = raw::NameEvent;
    type Err = InvalidInput;

    fn try_from_raw(raw: Self::Raw) -> Result<Self, Self::Err> {
        let content = TryFromRaw::try_from_raw(raw.content)?;
        let prev_content = raw.prev_content.map(TryFromRaw::try_from_raw).transpose()?;

        Ok(NameEvent {
            content,
            event_id: raw.event_id,
            origin_server_ts: raw.origin_server_ts,
            prev_content,
            room_id: raw.room_id,
            sender: raw.sender,
            state_key: raw.state_key,
            unsigned: raw.unsigned,
        })
    }
}

impl TryFromRaw for NameEventContent {
    type Raw = raw::NameEventContent;

    type Err = InvalidInput;

    fn try_from_raw(raw: raw::NameEventContent) -> Result<Self, Self::Err> {
        match raw.name {
            None => Ok(NameEventContent { name: None }),
            Some(name) => NameEventContent::new(name),
        }
    }
}

impl_state_event!(NameEvent, NameEventContent, EventType::RoomName);

impl NameEventContent {
    /// Create a new `NameEventContent` with the given name.
    ///
    /// # Errors
    ///
    /// `InvalidInput` will be returned if the name is more than 255 bytes.
    pub fn new(name: String) -> Result<Self, InvalidInput> {
        match name.len() {
            0 => Ok(Self { name: None }),
            1..=255 => Ok(Self { name: Some(name) }),
            _ => Err(InvalidInput(
                "a room name cannot be more than 255 bytes".to_string(),
            )),
        }
    }

    /// The name of the room, if any.
    pub fn name(&self) -> Option<&str> {
        self.name.as_ref().map(String::as_ref)
    }
}

pub(crate) mod raw {
    use super::*;

    /// A human-friendly room name designed to be displayed to the end-user.
    #[derive(Clone, Debug, PartialEq, Deserialize)]
    pub struct NameEvent {
        /// The event's content.
        pub content: NameEventContent,

        /// The unique identifier for the event.
        pub event_id: EventId,

        /// Time on originating homeserver when this event was sent.
        #[serde(with = "ruma_serde::time::ms_since_unix_epoch")]
        pub origin_server_ts: SystemTime,

        /// The previous content for this state key, if any.
        pub prev_content: Option<NameEventContent>,

        /// The unique identifier for the room associated with this event.
        pub room_id: Option<RoomId>,

        /// The unique identifier for the user who sent this event.
        pub sender: UserId,

        /// A key that determines which piece of room state the event represents.
        pub state_key: String,

        /// Additional key-value pairs not signed by the homeserver.
        #[serde(default)]
        pub unsigned: UnsignedData,
    }

    /// The payload of a `NameEvent`.
    #[derive(Clone, Debug, PartialEq, Deserialize)]
    pub struct NameEventContent {
        /// The name of the room. This MUST NOT exceed 255 bytes.
        // The spec says "A room with an m.room.name event with an absent, null, or empty name field
        // should be treated the same as a room with no m.room.name event."
        #[serde(default, deserialize_with = "ruma_serde::empty_string_as_none")]
        pub(crate) name: Option<String>,
    }
}

#[cfg(test)]
mod tests {
    use std::{
        convert::TryFrom,
        iter::FromIterator,
        time::{Duration, UNIX_EPOCH},
    };

    use js_int::Int;
    use ruma_identifiers::{EventId, RoomId, UserId};
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use crate::{EventJson, UnsignedData};

    use super::{NameEvent, NameEventContent};

    #[test]
    fn serialization_with_optional_fields_as_none() {
        let name_event = NameEvent {
            content: NameEventContent {
                name: Some("The room name".to_string()),
            },
            event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
            origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
            prev_content: None,
            room_id: None,
            sender: UserId::try_from("@carl:example.com").unwrap(),
            state_key: "".to_string(),
            unsigned: UnsignedData::default(),
        };

        let actual = to_json_value(&name_event).unwrap();
        let expected = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn serialization_with_all_fields() {
        let name_event = NameEvent {
            content: NameEventContent {
                name: Some("The room name".to_string()),
            },
            event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
            origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
            prev_content: Some(NameEventContent {
                name: Some("The old name".to_string()),
            }),
            room_id: Some(RoomId::try_from("!n8f893n9:example.com").unwrap()),
            sender: UserId::try_from("@carl:example.com").unwrap(),
            state_key: "".to_string(),
            unsigned: UnsignedData {
                age: Some(Int::from(100)),
                ..UnsignedData::default()
            },
        };

        let actual = to_json_value(&name_event).unwrap();
        let expected = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "prev_content": { "name": "The old name" },
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name",
            "unsigned": {
                "age": 100
            }
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn absent_field_as_none() {
        let json_data = json!({
            "content": {},
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<EventJson<NameEvent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn name_fails_validation_when_too_long() {
        // "XXXX .." 256 times
        let long_string: String = String::from_iter(std::iter::repeat('X').take(256));
        assert_eq!(long_string.len(), 256);

        let long_content_json_string: String =
            serde_json::json!({ "name": &long_string }).to_string();

        let from_raw: EventJson<NameEventContent> =
            serde_json::from_str(&long_content_json_string).unwrap();

        let result = from_raw.deserialize();
        assert!(result.is_err(), "Result should be invalid: {:?}", result);
    }

    #[test]
    fn json_with_empty_name_creates_content_as_none() {
        let long_content_json_string: String = serde_json::json!({ "name": "" }).to_string();

        let from_raw: EventJson<NameEventContent> =
            serde_json::from_str(&long_content_json_string).unwrap();
        assert_eq!(
            from_raw.deserialize().unwrap(),
            NameEventContent { name: None }
        );
    }

    #[test]
    fn new_with_empty_name_creates_content_as_none() {
        assert_eq!(
            NameEventContent::new(String::new()).unwrap(),
            NameEventContent { name: None }
        );
    }

    #[test]
    fn null_field_as_none() {
        let json_data = json!({
            "content": {
                "name": null
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<EventJson<NameEvent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn empty_string_as_none() {
        let json_data = json!({
            "content": {
                "name": ""
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<EventJson<NameEvent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn nonempty_field_as_some() {
        let name = Some("The room name".to_string());
        let json_data = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });

        assert_eq!(
            from_json_value::<EventJson<NameEvent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            name
        );
    }
}
<commit_msg>Update room::name tests<commit_after>//! Types for the *m.room.name* event.

use std::time::SystemTime;

use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

use crate::{EventType, InvalidInput, TryFromRaw, UnsignedData};

/// A human-friendly room name designed to be displayed to the end-user.
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename = "m.room.name", tag = "type")]
pub struct NameEvent {
    /// The event's content.
    pub content: NameEventContent,

    /// The unique identifier for the event.
    pub event_id: EventId,

    /// Time on originating homeserver when this event was sent.
    #[serde(with = "ruma_serde::time::ms_since_unix_epoch")]
    pub origin_server_ts: SystemTime,

    /// The previous content for this state key, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prev_content: Option<NameEventContent>,

    /// The unique identifier for the room associated with this event.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub room_id: Option<RoomId>,

    /// The unique identifier for the user who sent this event.
    pub sender: UserId,

    /// A key that determines which piece of room state the event represents.
    pub state_key: String,

    /// Additional key-value pairs not signed by the homeserver.
    #[serde(skip_serializing_if = "ruma_serde::is_default")]
    pub unsigned: UnsignedData,
}

/// The payload for `NameEvent`.
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct NameEventContent {
    /// The name of the room. This MUST NOT exceed 255 bytes.
    pub(crate) name: Option<String>,
}

impl TryFromRaw for NameEvent {
    type Raw = raw::NameEvent;
    type Err = InvalidInput;

    fn try_from_raw(raw: Self::Raw) -> Result<Self, Self::Err> {
        let content = TryFromRaw::try_from_raw(raw.content)?;
        let prev_content = raw.prev_content.map(TryFromRaw::try_from_raw).transpose()?;

        Ok(NameEvent {
            content,
            event_id: raw.event_id,
            origin_server_ts: raw.origin_server_ts,
            prev_content,
            room_id: raw.room_id,
            sender: raw.sender,
            state_key: raw.state_key,
            unsigned: raw.unsigned,
        })
    }
}

impl TryFromRaw for NameEventContent {
    type Raw = raw::NameEventContent;

    type Err = InvalidInput;

    fn try_from_raw(raw: raw::NameEventContent) -> Result<Self, Self::Err> {
        match raw.name {
            None => Ok(NameEventContent { name: None }),
            Some(name) => NameEventContent::new(name),
        }
    }
}

impl_state_event!(NameEvent, NameEventContent, EventType::RoomName);

impl NameEventContent {
    /// Create a new `NameEventContent` with the given name.
    ///
    /// # Errors
    ///
    /// `InvalidInput` will be returned if the name is more than 255 bytes.
    pub fn new(name: String) -> Result<Self, InvalidInput> {
        match name.len() {
            0 => Ok(Self { name: None }),
            1..=255 => Ok(Self { name: Some(name) }),
            _ => Err(InvalidInput(
                "a room name cannot be more than 255 bytes".to_string(),
            )),
        }
    }

    /// The name of the room, if any.
    pub fn name(&self) -> Option<&str> {
        self.name.as_ref().map(String::as_ref)
    }
}

pub(crate) mod raw {
    use super::*;

    /// A human-friendly room name designed to be displayed to the end-user.
    #[derive(Clone, Debug, PartialEq, Deserialize)]
    pub struct NameEvent {
        /// The event's content.
        pub content: NameEventContent,

        /// The unique identifier for the event.
        pub event_id: EventId,

        /// Time on originating homeserver when this event was sent.
        #[serde(with = "ruma_serde::time::ms_since_unix_epoch")]
        pub origin_server_ts: SystemTime,

        /// The previous content for this state key, if any.
        pub prev_content: Option<NameEventContent>,

        /// The unique identifier for the room associated with this event.
        pub room_id: Option<RoomId>,

        /// The unique identifier for the user who sent this event.
        pub sender: UserId,

        /// A key that determines which piece of room state the event represents.
        pub state_key: String,

        /// Additional key-value pairs not signed by the homeserver.
        #[serde(default)]
        pub unsigned: UnsignedData,
    }

    /// The payload of a `NameEvent`.
    #[derive(Clone, Debug, PartialEq, Deserialize)]
    pub struct NameEventContent {
        /// The name of the room. This MUST NOT exceed 255 bytes.
        // The spec says "A room with an m.room.name event with an absent, null, or empty name field
        // should be treated the same as a room with no m.room.name event."
        #[serde(default, deserialize_with = "ruma_serde::empty_string_as_none")]
        pub(crate) name: Option<String>,
    }
}

#[cfg(test)]
mod tests {
    use std::{
        convert::TryFrom,
        iter::FromIterator,
        time::{Duration, UNIX_EPOCH},
    };

    use js_int::Int;
    use matches::assert_matches;
    use ruma_identifiers::{EventId, RoomId, UserId};
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use crate::{EventJson, UnsignedData};

    use super::{NameEvent, NameEventContent};

    #[test]
    fn serialization_with_optional_fields_as_none() {
        let name_event = NameEvent {
            content: NameEventContent {
                name: Some("The room name".to_string()),
            },
            event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
            origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
            prev_content: None,
            room_id: None,
            sender: UserId::try_from("@carl:example.com").unwrap(),
            state_key: "".to_string(),
            unsigned: UnsignedData::default(),
        };

        let actual = to_json_value(&name_event).unwrap();
        let expected = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn serialization_with_all_fields() {
        let name_event = NameEvent {
            content: NameEventContent {
                name: Some("The room name".to_string()),
            },
            event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(),
            origin_server_ts: UNIX_EPOCH + Duration::from_millis(1),
            prev_content: Some(NameEventContent {
                name: Some("The old name".to_string()),
            }),
            room_id: Some(RoomId::try_from("!n8f893n9:example.com").unwrap()),
            sender: UserId::try_from("@carl:example.com").unwrap(),
            state_key: "".to_string(),
            unsigned: UnsignedData {
                age: Some(Int::from(100)),
                ..UnsignedData::default()
            },
        };

        let actual = to_json_value(&name_event).unwrap();
        let expected = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "prev_content": { "name": "The old name" },
            "room_id": "!n8f893n9:example.com",
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name",
            "unsigned": {
                "age": 100
            }
        });

        assert_eq!(actual, expected);
    }

    #[test]
    fn absent_field_as_none() {
        let json_data = json!({
            "content": {},
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<EventJson<NameEvent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn name_fails_validation_when_too_long() {
        // "XXXX .." 256 times
        let long_string: String = String::from_iter(std::iter::repeat('X').take(256));
        assert_eq!(long_string.len(), 256);

        let long_content_json = json!({ "name": &long_string });
        let from_raw: EventJson<NameEventContent> = from_json_value(long_content_json).unwrap();

        let result = from_raw.deserialize();
        assert!(result.is_err(), "Result should be invalid: {:?}", result);
    }

    #[test]
    fn json_with_empty_name_creates_content_as_none() {
        let long_content_json = json!({ "name": "" });
        let from_raw: EventJson<NameEventContent> = from_json_value(long_content_json).unwrap();
        assert_matches!(
            from_raw.deserialize().unwrap(),
            NameEventContent { name: None }
        );
    }

    #[test]
    fn new_with_empty_name_creates_content_as_none() {
        assert_matches!(
            NameEventContent::new(String::new()).unwrap(),
            NameEventContent { name: None }
        );
    }

    #[test]
    fn null_field_as_none() {
        let json_data = json!({
            "content": {
                "name": null
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<EventJson<NameEvent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn empty_string_as_none() {
        let json_data = json!({
            "content": {
                "name": ""
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });
        assert_eq!(
            from_json_value::<EventJson<NameEvent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            None
        );
    }

    #[test]
    fn nonempty_field_as_some() {
        let name = Some("The room name".to_string());
        let json_data = json!({
            "content": {
                "name": "The room name"
            },
            "event_id": "$h29iv0s8:example.com",
            "origin_server_ts": 1,
            "sender": "@carl:example.com",
            "state_key": "",
            "type": "m.room.name"
        });

        assert_eq!(
            from_json_value::<EventJson<NameEvent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap()
                .content
                .name,
            name
        );
    }
}
<|endoftext|>