<commit_before>use std::path::Path;

use ops::{self, ExecEngine, CompileFilter};
use util::{CargoResult, human, process, ProcessError};
use core::source::Source;
use sources::PathSource;

pub fn run(manifest_path: &Path,
           options: &ops::CompileOptions,
           args: &[String]) -> CargoResult<Option<ProcessError>> {
    let config = options.config;
    let mut src = try!(PathSource::for_path(&manifest_path.parent().unwrap(),
                                            config));
    try!(src.update());
    let root = try!(src.root_package());

    // Make sure that we're only running at most one binary. The `compile` step
    // will verify that we're buliding at least one binary, so we don't check
    // for that form of existence here.
    let mut bins = root.manifest().targets().iter().filter(|a| {
        options.filter.matches(a) && !a.is_lib() && !a.is_custom_build()
    });
    let _ = bins.next();
    if bins.next().is_some() {
        match options.filter {
            CompileFilter::Everything => {
                return Err(human("`cargo run` requires that a project only have \
                                  one executable; use the `--bin` option to \
                                  specify which one to run"))
            }
            CompileFilter::Only { .. } => {
                return Err(human("`cargo run` can run at most one executable, \
                                  but multiple were specified"))
            }
        }
    }

    let compile = try!(ops::compile(manifest_path, options));
    let exe = &compile.binaries[0];
    let exe = match exe.relative_from(config.cwd()) {
        Some(path) => path,
        None => &**exe,
    };
    let mut process = try!(compile.target_process(exe, &root))
                                  .into_process_builder();
    process.args(args).cwd(config.cwd());

    try!(config.shell().status("Running", process.to_string()));
    Ok(process.exec().err())
}
<commit_msg>Don't fail `cargo run` with bins + examples<commit_after>use std::path::Path;

use ops::{self, ExecEngine, CompileFilter};
use util::{CargoResult, human, process, ProcessError};
use core::source::Source;
use sources::PathSource;

pub fn run(manifest_path: &Path,
           options: &ops::CompileOptions,
           args: &[String]) -> CargoResult<Option<ProcessError>> {
    let config = options.config;
    let mut src = try!(PathSource::for_path(&manifest_path.parent().unwrap(),
                                            config));
    try!(src.update());
    let root = try!(src.root_package());

    // Make sure that we're only running at most one binary. The `compile` step
    // will verify that we're buliding at least one binary, so we don't check
    // for that form of existence here.
    let mut bins = root.manifest().targets().iter().filter(|a| {
        !a.is_lib() && !a.is_custom_build() && match options.filter {
            CompileFilter::Everything => a.is_bin(),
            CompileFilter::Only { .. } => options.filter.matches(a),
        }
    });
    let _ = bins.next();
    if bins.next().is_some() {
        match options.filter {
            CompileFilter::Everything => {
                return Err(human("`cargo run` requires that a project only have \
                                  one executable; use the `--bin` option to \
                                  specify which one to run"))
            }
            CompileFilter::Only { .. } => {
                return Err(human("`cargo run` can run at most one executable, \
                                  but multiple were specified"))
            }
        }
    }

    let compile = try!(ops::compile(manifest_path, options));
    let exe = &compile.binaries[0];
    let exe = match exe.relative_from(config.cwd()) {
        Some(path) => path,
        None => &**exe,
    };
    let mut process = try!(compile.target_process(exe, &root))
                                  .into_process_builder();
    process.args(args).cwd(config.cwd());

    try!(config.shell().status("Running", process.to_string()));
    Ok(process.exec().err())
}
<|endoftext|>


<commit_before>//! Implements a command for sending events to Sentry.
use std::borrow::Cow;
use std::env;

use clap::{App, Arg, ArgMatches};
use failure::{err_msg, Error};
use itertools::Itertools;
use sentry::protocol::{Event, Level, LogEntry, User};
use serde_json::Value;
use username::get_user_name;

use crate::config::Config;
use crate::utils::event::{attach_logfile, get_sdk_info, with_sentry_client};
use crate::utils::releases::detect_release_name;

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("Send a manual event to Sentry.")
        .long_about(
            "Send a manual event to Sentry.{n}{n}\
             NOTE: This command will validate input parameters and attempt to send an event to \
             Sentry. Due to network errors, rate limits or sampling the event is not guaranteed to \
             actually arrive. Check debug output for transmission errors by passing --log-level=\
             debug or setting `SENTRY_LOG_LEVEL=debug`.",
        )
        .arg(
            Arg::with_name("level")
                .value_name("LEVEL")
                .long("level")
                .short("l")
                .help("Optional event severity/log level. [defaults to 'error']"),
        )
        .arg(
            Arg::with_name("release")
                .value_name("RELEASE")
                .long("release")
                .short("r")
                .help("Optional identifier of the release."),
        )
        .arg(
            Arg::with_name("dist")
                .value_name("DISTRIBUTION")
                .long("dist")
                .short("d")
                .help("Set the distribution."),
        )
        .arg(
            Arg::with_name("environment")
                .value_name("ENVIRONMENT")
                .long("env")
                .short("E")
                .help("Send with a specific environment."),
        )
        .arg(
            Arg::with_name("no_environ")
                .long("no-environ")
                .help("Do not send environment variables along"),
        )
        .arg(
            Arg::with_name("message")
                .value_name("MESSAGE")
                .long("message")
                .short("m")
                .multiple(true)
                .number_of_values(1)
                .help("The event message."),
        )
        .arg(
            Arg::with_name("message_args")
                .value_name("MESSAGE_ARG")
                .long("message-arg")
                .short("a")
                .multiple(true)
                .number_of_values(1)
                .help("Arguments for the event message."),
        )
        .arg(
            Arg::with_name("platform")
                .value_name("PLATFORM")
                .long("platform")
                .short("p")
                .help("Override the default 'other' platform specifier."),
        )
        .arg(
            Arg::with_name("tags")
                .value_name("KEY:VALUE")
                .long("tag")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .help("Add a tag (key:value) to the event."),
        )
        .arg(
            Arg::with_name("extra")
                .value_name("KEY:VALUE")
                .long("extra")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .help("Add extra information (key:value) to the event."),
        )
        .arg(
            Arg::with_name("user_data")
                .value_name("KEY:VALUE")
                .long("user")
                .short("u")
                .multiple(true)
                .number_of_values(1)
                .help(
                    "Add user information (key:value) to the event. \
                     [eg: id:42, username:foo]",
                ),
        )
        .arg(
            Arg::with_name("fingerprint")
                .value_name("FINGERPRINT")
                .long("fingerprint")
                .short("f")
                .multiple(true)
                .number_of_values(1)
                .help("Change the fingerprint of the event."),
        )
        .arg(
            Arg::with_name("logfile")
                .value_name("PATH")
                .long("logfile")
                .help("Send a logfile as breadcrumbs with the event (last 100 records)"),
        )
        .arg(
            Arg::with_name("with_categories")
                .long("with-categories")
                .help("Parses off a leading category for breadcrumbs from the logfile"),
        )
}

pub fn execute<'a>(matches: &ArgMatches<'a>) -> Result<(), Error> {
    let config = Config::current();
    let mut event = Event::default();

    event.sdk = Some(get_sdk_info());

    event.level = matches
        .value_of("level")
        .and_then(|l| l.parse().ok())
        .unwrap_or(Level::Error);

    if let Some(release) = matches.value_of("release") {
        event.release = Some(release.to_string().into());
    } else {
        event.release = detect_release_name().ok().map(Cow::from);
    }

    event.dist = matches.value_of("dist").map(|x| x.to_string().into());
    event.platform = matches
        .value_of("platform")
        .unwrap_or("other")
        .to_string()
        .into();
    event.environment = matches
        .value_of("environment")
        .map(|x| x.to_string().into());

    if let Some(mut lines) = matches.values_of("message") {
        event.logentry = Some(LogEntry {
            message: lines.join("\n"),
            params: matches
                .values_of("message_args")
                .map(|args| args.map(|x| x.into()).collect())
                .unwrap_or_default(),
        });
    }

    for tag in matches.values_of("tags").unwrap_or_default() {
        let mut split = tag.splitn(2, ':');
        let key = split.next().ok_or_else(|| err_msg("missing tag key"))?;
        let value = split.next().ok_or_else(|| err_msg("missing tag value"))?;
        event.tags.insert(key.into(), value.into());
    }

    if !matches.is_present("no_environ") {
        event.extra.insert(
            "environ".into(),
            Value::Object(env::vars().map(|(k, v)| (k, Value::String(v))).collect()),
        );
    }

    for pair in matches.values_of("extra").unwrap_or_default() {
        let mut split = pair.splitn(2, ':');
        let key = split.next().ok_or_else(|| err_msg("missing extra key"))?;
        let value = split.next().ok_or_else(|| err_msg("missing extra value"))?;
        event.extra.insert(key.into(), Value::String(value.into()));
    }

    if let Some(user_data) = matches.values_of("user_data") {
        let mut user = User::default();
        for pair in user_data {
            let mut split = pair.splitn(2, ':');
            let key = split.next().ok_or_else(|| err_msg("missing user key"))?;
            let value = split.next().ok_or_else(|| err_msg("missing user value"))?;

            match key {
                "id" => user.id = Some(value.into()),
                "email" => user.email = Some(value.into()),
                "ip_address" => user.ip_address = Some(value.parse()?),
                "username" => user.username = Some(value.into()),
                _ => {
                    user.other.insert(key.into(), value.into());
                }
            };
        }

        user.ip_address.get_or_insert(Default::default());
        event.user = Some(user);
    } else {
        event.user = get_user_name().ok().map(|n| User {
            username: Some(n),
            ip_address: Some(Default::default()),
            ..Default::default()
        });
    }

    if let Some(fingerprint) = matches.values_of("fingerprint") {
        event.fingerprint = fingerprint
            .map(|x| x.to_string().into())
            .collect::<Vec<_>>()
            .into();
    }

    if let Some(logfile) = matches.value_of("logfile") {
        attach_logfile(&mut event, logfile, matches.is_present("with_categories"))?;
    }

    let id = with_sentry_client(config.get_dsn()?, |c| c.capture_event(event, None));
    println!("{}", id);

    Ok(())
}
<commit_msg>docs: Provide better explanation for --with-categories flag (#678)<commit_after>//! Implements a command for sending events to Sentry.
use std::borrow::Cow;
use std::env;

use clap::{App, Arg, ArgMatches};
use failure::{err_msg, Error};
use itertools::Itertools;
use sentry::protocol::{Event, Level, LogEntry, User};
use serde_json::Value;
use username::get_user_name;

use crate::config::Config;
use crate::utils::event::{attach_logfile, get_sdk_info, with_sentry_client};
use crate::utils::releases::detect_release_name;

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("Send a manual event to Sentry.")
        .long_about(
            "Send a manual event to Sentry.{n}{n}\
             NOTE: This command will validate input parameters and attempt to send an event to \
             Sentry. Due to network errors, rate limits or sampling the event is not guaranteed to \
             actually arrive. Check debug output for transmission errors by passing --log-level=\
             debug or setting `SENTRY_LOG_LEVEL=debug`.",
        )
        .arg(
            Arg::with_name("level")
                .value_name("LEVEL")
                .long("level")
                .short("l")
                .help("Optional event severity/log level. [defaults to 'error']"),
        )
        .arg(
            Arg::with_name("release")
                .value_name("RELEASE")
                .long("release")
                .short("r")
                .help("Optional identifier of the release."),
        )
        .arg(
            Arg::with_name("dist")
                .value_name("DISTRIBUTION")
                .long("dist")
                .short("d")
                .help("Set the distribution."),
        )
        .arg(
            Arg::with_name("environment")
                .value_name("ENVIRONMENT")
                .long("env")
                .short("E")
                .help("Send with a specific environment."),
        )
        .arg(
            Arg::with_name("no_environ")
                .long("no-environ")
                .help("Do not send environment variables along"),
        )
        .arg(
            Arg::with_name("message")
                .value_name("MESSAGE")
                .long("message")
                .short("m")
                .multiple(true)
                .number_of_values(1)
                .help("The event message."),
        )
        .arg(
            Arg::with_name("message_args")
                .value_name("MESSAGE_ARG")
                .long("message-arg")
                .short("a")
                .multiple(true)
                .number_of_values(1)
                .help("Arguments for the event message."),
        )
        .arg(
            Arg::with_name("platform")
                .value_name("PLATFORM")
                .long("platform")
                .short("p")
                .help("Override the default 'other' platform specifier."),
        )
        .arg(
            Arg::with_name("tags")
                .value_name("KEY:VALUE")
                .long("tag")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .help("Add a tag (key:value) to the event."),
        )
        .arg(
            Arg::with_name("extra")
                .value_name("KEY:VALUE")
                .long("extra")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .help("Add extra information (key:value) to the event."),
        )
        .arg(
            Arg::with_name("user_data")
                .value_name("KEY:VALUE")
                .long("user")
                .short("u")
                .multiple(true)
                .number_of_values(1)
                .help(
                    "Add user information (key:value) to the event. \
                     [eg: id:42, username:foo]",
                ),
        )
        .arg(
            Arg::with_name("fingerprint")
                .value_name("FINGERPRINT")
                .long("fingerprint")
                .short("f")
                .multiple(true)
                .number_of_values(1)
                .help("Change the fingerprint of the event."),
        )
        .arg(
            Arg::with_name("logfile")
                .value_name("PATH")
                .long("logfile")
                .help("Send a logfile as breadcrumbs with the event (last 100 records)"),
        )
        .arg(
            Arg::with_name("with_categories")
                .long("with-categories")
                .help("Parses off a leading category for breadcrumbs from the logfile")
                .long_help(
                    "When logfile is provided, this flag will try to assign correct level \
                    to extracted log breadcrumbs. It uses standard log format of \"category: message\". \
                    eg. \"INFO: Something broke\" will be parsed as a breadcrumb \
                    \"{\"level\": \"info\", \"message\": \"Something broke\"}\"")
        )
}

pub fn execute<'a>(matches: &ArgMatches<'a>) -> Result<(), Error> {
    let config = Config::current();
    let mut event = Event::default();

    event.sdk = Some(get_sdk_info());

    event.level = matches
        .value_of("level")
        .and_then(|l| l.parse().ok())
        .unwrap_or(Level::Error);

    if let Some(release) = matches.value_of("release") {
        event.release = Some(release.to_string().into());
    } else {
        event.release = detect_release_name().ok().map(Cow::from);
    }

    event.dist = matches.value_of("dist").map(|x| x.to_string().into());
    event.platform = matches
        .value_of("platform")
        .unwrap_or("other")
        .to_string()
        .into();
    event.environment = matches
        .value_of("environment")
        .map(|x| x.to_string().into());

    if let Some(mut lines) = matches.values_of("message") {
        event.logentry = Some(LogEntry {
            message: lines.join("\n"),
            params: matches
                .values_of("message_args")
                .map(|args| args.map(|x| x.into()).collect())
                .unwrap_or_default(),
        });
    }

    for tag in matches.values_of("tags").unwrap_or_default() {
        let mut split = tag.splitn(2, ':');
        let key = split.next().ok_or_else(|| err_msg("missing tag key"))?;
        let value = split.next().ok_or_else(|| err_msg("missing tag value"))?;
        event.tags.insert(key.into(), value.into());
    }

    if !matches.is_present("no_environ") {
        event.extra.insert(
            "environ".into(),
            Value::Object(env::vars().map(|(k, v)| (k, Value::String(v))).collect()),
        );
    }

    for pair in matches.values_of("extra").unwrap_or_default() {
        let mut split = pair.splitn(2, ':');
        let key = split.next().ok_or_else(|| err_msg("missing extra key"))?;
        let value = split.next().ok_or_else(|| err_msg("missing extra value"))?;
        event.extra.insert(key.into(), Value::String(value.into()));
    }

    if let Some(user_data) = matches.values_of("user_data") {
        let mut user = User::default();
        for pair in user_data {
            let mut split = pair.splitn(2, ':');
            let key = split.next().ok_or_else(|| err_msg("missing user key"))?;
            let value = split.next().ok_or_else(|| err_msg("missing user value"))?;

            match key {
                "id" => user.id = Some(value.into()),
                "email" => user.email = Some(value.into()),
                "ip_address" => user.ip_address = Some(value.parse()?),
                "username" => user.username = Some(value.into()),
                _ => {
                    user.other.insert(key.into(), value.into());
                }
            };
        }

        user.ip_address.get_or_insert(Default::default());
        event.user = Some(user);
    } else {
        event.user = get_user_name().ok().map(|n| User {
            username: Some(n),
            ip_address: Some(Default::default()),
            ..Default::default()
        });
    }

    if let Some(fingerprint) = matches.values_of("fingerprint") {
        event.fingerprint = fingerprint
            .map(|x| x.to_string().into())
            .collect::<Vec<_>>()
            .into();
    }

    if let Some(logfile) = matches.value_of("logfile") {
        attach_logfile(&mut event, logfile, matches.is_present("with_categories"))?;
    }

    let id = with_sentry_client(config.get_dsn()?, |c| c.capture_event(event, None));
    println!("{}", id);

    Ok(())
}
<|endoftext|>


<commit_before>import std::{smallintmap, option};
import syntax::ast::*;
import syntax::ast_util;
import syntax::{visit, codemap};

tag ast_node {
    node_item(@item);
    node_obj_ctor(@item);
    node_native_item(@native_item);
    node_expr(@expr);
    // Locals are numbered, because the alias analysis needs to know in which
    // order they are introduced.
    node_arg(arg, uint);
    node_local(uint);
}

type map = std::map::hashmap<node_id, ast_node>;
type ctx = @{map: map, mutable local_id: uint};

fn map_crate(c: crate) -> map {
    // FIXME: This is using an adapter to convert the smallintmap
    // interface to the hashmap interface. It would be better to just
    // convert everything to use the smallintmap.
    let cx = @{map: new_smallintmap_int_adapter::<ast_node>(),
               mutable local_id: 0u};

    let v_map = visit::mk_simple_visitor
        (@{visit_item: bind map_item(cx, _),
           visit_native_item: bind map_native_item(cx, _),
           visit_expr: bind map_expr(cx, _),
           visit_fn: bind map_fn(cx, _, _, _, _, _),
           visit_local: bind map_local(cx, _),
           visit_arm: bind map_arm(cx, _)
           with *visit::default_simple_visitor()});
    visit::visit_crate(c, (), v_map);
    ret cx.map;
}

fn map_fn(cx: ctx, f: _fn, _tp: [ty_param], _sp: codemap::span,
          _name: fn_ident, _id: node_id) {
    for a in f.decl.inputs {
        cx.map.insert(a.id, node_arg(a, cx.local_id));
        cx.local_id += 1u;
    }
}

fn map_local(cx: ctx, loc: @local) {
    ast_util::pat_bindings(loc.node.pat) {|p|
        cx.map.insert(p.id, node_local(cx.local_id));
        cx.local_id += 1u;
    };
}

fn map_arm(cx: ctx, arm: arm) {
    ast_util::pat_bindings(arm.pats[0]) {|p|
        cx.map.insert(p.id, node_local(cx.local_id));
        cx.local_id += 1u;
    };
}

fn map_item(cx: ctx, i: @item) {
    cx.map.insert(i.id, node_item(i));
    alt i.node {
      item_obj(_, _, ctor_id) { cx.map.insert(ctor_id, node_obj_ctor(i)); }
      _ { }
    }
}

fn map_native_item(cx: ctx, i: @native_item) {
    cx.map.insert(i.id, node_native_item(i));
}

fn map_expr(cx: ctx, ex: @expr) {
    cx.map.insert(ex.id, node_expr(ex));
}

fn new_smallintmap_int_adapter<V>() -> std::map::hashmap<int, V> {
    let key_idx = fn (&&key: int) -> uint { key as uint };
    let idx_key = fn (idx: uint) -> int { idx as int };
    ret new_smallintmap_adapter(key_idx, idx_key);
}

// This creates an object with the hashmap interface backed
// by the smallintmap type, because I don't want to go through
// the entire codebase adapting all the callsites to the different
// interface.
// FIXME: hashmap and smallintmap should support the same interface.
fn new_smallintmap_adapter<K, V>(key_idx: fn(K) -> uint,
                                   idx_key: fn(uint) -> K)
    -> std::map::hashmap<K, V> {

    obj adapter<shar K, shar V>(map: smallintmap::smallintmap<V>,
                                key_idx: fn(K) -> uint,
                                idx_key: fn(uint) -> K) {

        fn size() -> uint { fail }

        fn insert(key: K, value: V) -> bool {
            let exists = smallintmap::contains_key(map, key_idx(key));
            smallintmap::insert(map, key_idx(key), value);
            ret !exists;
        }

        fn contains_key(key: K) -> bool {
            ret smallintmap::contains_key(map, key_idx(key));
        }

        fn get(key: K) -> V { ret smallintmap::get(map, key_idx(key)); }

        fn find(key: K) -> option::t<V> {
            ret smallintmap::find(map, key_idx(key));
        }

        fn remove(_key: K) -> option::t<V> { fail }

        fn rehash() { fail }

        fn items(it: block(K, V)) {
            let idx = 0u;
            for item in map.v {
                alt item {
                  option::some(elt) {
                    it(idx_key(idx), elt);
                  }
                  option::none. { }
                }
                idx += 1u;
            }
        }
        fn keys(it: block(K)) {
            let idx = 0u;
            for item in map.v {
                if item != option::none { it(idx_key(idx)); }
                idx += 1u;
            }
        }
        fn values(it: block(V)) {
            for item in map.v {
                alt item { option::some(elt) { it(elt); } _ {} }
            }
        }
    }

    let map = smallintmap::mk::<V>();
    ret adapter(map, key_idx, idx_key);
}

fn node_span(node: ast_node) -> codemap::span {
    alt node {
      node_item(item) { item.span }
      node_obj_ctor(item) { item.span }
      node_native_item(nitem) { nitem.span }
      node_expr(expr) { expr.span }
    }
}

#[cfg(test)]
mod test {
    import syntax::ast_util;

    #[test]
    fn test_node_span_item() {
        let expected: codemap::span = ast_util::mk_sp(20u, 30u);
        let node =
            node_item(@{ident: "test",
                        attrs: [],
                        id: 0,
                        node: item_mod({view_items: [], items: []}),
                        span: expected});
        assert (node_span(node) == expected);
    }

    #[test]
    fn test_node_span_obj_ctor() {
        let expected: codemap::span = ast_util::mk_sp(20u, 30u);
        let node =
            node_obj_ctor(@{ident: "test",
                            attrs: [],
                            id: 0,
                            node: item_mod({view_items: [], items: []}),
                            span: expected});
        assert (node_span(node) == expected);
    }

    #[test]
    fn test_node_span_native_item() {
        let expected: codemap::span = ast_util::mk_sp(20u, 30u);
        let node =
            node_native_item(@{ident: "test",
                               attrs: [],
                               node: native_item_ty,
                               id: 0,
                               span: expected});
        assert (node_span(node) == expected);
    }

    #[test]
    fn test_node_span_expr() {
        let expected: codemap::span = ast_util::mk_sp(20u, 30u);
        let node = node_expr(@{id: 0, node: expr_break, span: expected});
        assert (node_span(node) == expected);
    }
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>temporarily disable these tests b/c they crash rustc<commit_after>import std::{smallintmap, option};
import syntax::ast::*;
import syntax::ast_util;
import syntax::{visit, codemap};

tag ast_node {
    node_item(@item);
    node_obj_ctor(@item);
    node_native_item(@native_item);
    node_expr(@expr);
    // Locals are numbered, because the alias analysis needs to know in which
    // order they are introduced.
    node_arg(arg, uint);
    node_local(uint);
}

type map = std::map::hashmap<node_id, ast_node>;
type ctx = @{map: map, mutable local_id: uint};

fn map_crate(c: crate) -> map {
    // FIXME: This is using an adapter to convert the smallintmap
    // interface to the hashmap interface. It would be better to just
    // convert everything to use the smallintmap.
    let cx = @{map: new_smallintmap_int_adapter::<ast_node>(),
               mutable local_id: 0u};

    let v_map = visit::mk_simple_visitor
        (@{visit_item: bind map_item(cx, _),
           visit_native_item: bind map_native_item(cx, _),
           visit_expr: bind map_expr(cx, _),
           visit_fn: bind map_fn(cx, _, _, _, _, _),
           visit_local: bind map_local(cx, _),
           visit_arm: bind map_arm(cx, _)
           with *visit::default_simple_visitor()});
    visit::visit_crate(c, (), v_map);
    ret cx.map;
}

fn map_fn(cx: ctx, f: _fn, _tp: [ty_param], _sp: codemap::span,
          _name: fn_ident, _id: node_id) {
    for a in f.decl.inputs {
        cx.map.insert(a.id, node_arg(a, cx.local_id));
        cx.local_id += 1u;
    }
}

fn map_local(cx: ctx, loc: @local) {
    ast_util::pat_bindings(loc.node.pat) {|p|
        cx.map.insert(p.id, node_local(cx.local_id));
        cx.local_id += 1u;
    };
}

fn map_arm(cx: ctx, arm: arm) {
    ast_util::pat_bindings(arm.pats[0]) {|p|
        cx.map.insert(p.id, node_local(cx.local_id));
        cx.local_id += 1u;
    };
}

fn map_item(cx: ctx, i: @item) {
    cx.map.insert(i.id, node_item(i));
    alt i.node {
      item_obj(_, _, ctor_id) { cx.map.insert(ctor_id, node_obj_ctor(i)); }
      _ { }
    }
}

fn map_native_item(cx: ctx, i: @native_item) {
    cx.map.insert(i.id, node_native_item(i));
}

fn map_expr(cx: ctx, ex: @expr) {
    cx.map.insert(ex.id, node_expr(ex));
}

fn new_smallintmap_int_adapter<V>() -> std::map::hashmap<int, V> {
    let key_idx = fn (&&key: int) -> uint { key as uint };
    let idx_key = fn (idx: uint) -> int { idx as int };
    ret new_smallintmap_adapter(key_idx, idx_key);
}

// This creates an object with the hashmap interface backed
// by the smallintmap type, because I don't want to go through
// the entire codebase adapting all the callsites to the different
// interface.
// FIXME: hashmap and smallintmap should support the same interface.
fn new_smallintmap_adapter<K, V>(key_idx: fn(K) -> uint,
                                   idx_key: fn(uint) -> K)
    -> std::map::hashmap<K, V> {

    obj adapter<shar K, shar V>(map: smallintmap::smallintmap<V>,
                                key_idx: fn(K) -> uint,
                                idx_key: fn(uint) -> K) {

        fn size() -> uint { fail }

        fn insert(key: K, value: V) -> bool {
            let exists = smallintmap::contains_key(map, key_idx(key));
            smallintmap::insert(map, key_idx(key), value);
            ret !exists;
        }

        fn contains_key(key: K) -> bool {
            ret smallintmap::contains_key(map, key_idx(key));
        }

        fn get(key: K) -> V { ret smallintmap::get(map, key_idx(key)); }

        fn find(key: K) -> option::t<V> {
            ret smallintmap::find(map, key_idx(key));
        }

        fn remove(_key: K) -> option::t<V> { fail }

        fn rehash() { fail }

        fn items(it: block(K, V)) {
            let idx = 0u;
            for item in map.v {
                alt item {
                  option::some(elt) {
                    it(idx_key(idx), elt);
                  }
                  option::none. { }
                }
                idx += 1u;
            }
        }
        fn keys(it: block(K)) {
            let idx = 0u;
            for item in map.v {
                if item != option::none { it(idx_key(idx)); }
                idx += 1u;
            }
        }
        fn values(it: block(V)) {
            for item in map.v {
                alt item { option::some(elt) { it(elt); } _ {} }
            }
        }
    }

    let map = smallintmap::mk::<V>();
    ret adapter(map, key_idx, idx_key);
}

fn node_span(node: ast_node) -> codemap::span {
    alt node {
      node_item(item) { item.span }
      node_obj_ctor(item) { item.span }
      node_native_item(nitem) { nitem.span }
      node_expr(expr) { expr.span }
    }
}

#[cfg(test)]
mod test {
    import syntax::ast_util;

    //FIXME NDM #[test]
    fn test_node_span_item() {
        let expected: codemap::span = ast_util::mk_sp(20u, 30u);
        let node =
            node_item(@{ident: "test",
                        attrs: [],
                        id: 0,
                        node: item_mod({view_items: [], items: []}),
                        span: expected});
        assert (node_span(node) == expected);
    }

    //FIXME NDM #[test]
    fn test_node_span_obj_ctor() {
        let expected: codemap::span = ast_util::mk_sp(20u, 30u);
        let node =
            node_obj_ctor(@{ident: "test",
                            attrs: [],
                            id: 0,
                            node: item_mod({view_items: [], items: []}),
                            span: expected});
        assert (node_span(node) == expected);
    }

    //FIXME NDM #[test]
    fn test_node_span_native_item() {
        let expected: codemap::span = ast_util::mk_sp(20u, 30u);
        let node =
            node_native_item(@{ident: "test",
                               attrs: [],
                               node: native_item_ty,
                               id: 0,
                               span: expected});
        assert (node_span(node) == expected);
    }

    //FIXME NDM #[test]
    fn test_node_span_expr() {
        let expected: codemap::span = ast_util::mk_sp(20u, 30u);
        let node = node_expr(@{id: 0, node: expr_break, span: expected});
        assert (node_span(node) == expected);
    }
}

// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>use jansson_sys::json_t;
use std::os::raw::{c_char, c_int, c_void, c_short};

#[repr(C)]
#[derive(Debug)]
pub struct janus_callbacks {
    pub push_event: extern "C" fn(
        handle: *mut janus_plugin_session,
        plugin: *mut janus_plugin,
        transaction: *const c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> c_int,
    pub relay_rtp: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtp),
    pub relay_rtcp: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtcp),
    pub relay_data: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_data),
    pub close_pc: extern "C" fn(handle: *mut janus_plugin_session),
    pub end_session: extern "C" fn(handle: *mut janus_plugin_session),
    pub events_is_enabled: extern "C" fn() -> c_int,
    pub notify_event: extern "C" fn(plugin: *mut janus_plugin, handle: *mut janus_plugin_session, event: *mut json_t),
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum janus_plugin_result_type {
    JANUS_PLUGIN_ERROR = -1,
    JANUS_PLUGIN_OK = 0,
    JANUS_PLUGIN_OK_WAIT = 1,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_session {
    pub gateway_handle: *mut c_void,
    pub plugin_handle: *mut c_void,
    pub stopped: c_int,
    pub ref_: crate::janus_refcount,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_result {
    pub type_: janus_plugin_result_type,
    pub text: *const c_char,
    pub content: *mut json_t,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtp_extensions {
    pub audio_level : c_char,
    pub audio_level_vad : c_char,
    pub video_rotation : c_short,
    pub video_back_camera : c_char,
    pub video_flipped : c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtp {
    pub video :  c_char,
    pub buffer : * c_char,
    pub length : c_short,
    pub extensions : janus_plugin_rtp_extensions,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtcp {
    pub video : c_char,
    pub buffer : * c_char,
    pub length : c_short,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_data {
    pub label : *mut c_char,
    pub binary : c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin {
    pub init: unsafe extern "C" fn(callback: *mut janus_callbacks, config_path: *const c_char) -> c_int,
    pub destroy: unsafe extern "C" fn(),
    pub get_api_compatibility: unsafe extern "C" fn() -> c_int,
    pub get_version: unsafe extern "C" fn() -> c_int,
    pub get_version_string: unsafe extern "C" fn() -> *const c_char,
    pub get_description: unsafe extern "C" fn() -> *const c_char,
    pub get_name: unsafe extern "C" fn() -> *const c_char,
    pub get_author: unsafe extern "C" fn() -> *const c_char,
    pub get_package: unsafe extern "C" fn() -> *const c_char,
    pub create_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub handle_message: unsafe extern "C" fn(
        handle: *mut janus_plugin_session,
        transaction: *mut c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> *mut janus_plugin_result,
    pub handle_admin_message: unsafe extern "C" fn(message: *mut json_t) -> *mut json_t,
    pub setup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub incoming_rtp: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtp),
    pub incoming_rtcp: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtcp),
    pub incoming_data: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_data),
    pub slow_link: unsafe extern "C" fn(handle: *mut janus_plugin_session, uplink: c_int, video: c_int),
    pub hangup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub destroy_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub query_session: unsafe extern "C" fn(handle: *mut janus_plugin_session) -> *mut json_t,
}

extern "C" {
    pub fn janus_plugin_result_new(type_: janus_plugin_result_type, text: *const c_char, content: *mut json_t) -> *mut janus_plugin_result;
    pub fn janus_plugin_result_destroy(result: *mut janus_plugin_result);
}
<commit_msg>use *mut for buffer<commit_after>use jansson_sys::json_t;
use std::os::raw::{c_char, c_int, c_void, c_short};

#[repr(C)]
#[derive(Debug)]
pub struct janus_callbacks {
    pub push_event: extern "C" fn(
        handle: *mut janus_plugin_session,
        plugin: *mut janus_plugin,
        transaction: *const c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> c_int,
    pub relay_rtp: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtp),
    pub relay_rtcp: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtcp),
    pub relay_data: extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_data),
    pub close_pc: extern "C" fn(handle: *mut janus_plugin_session),
    pub end_session: extern "C" fn(handle: *mut janus_plugin_session),
    pub events_is_enabled: extern "C" fn() -> c_int,
    pub notify_event: extern "C" fn(plugin: *mut janus_plugin, handle: *mut janus_plugin_session, event: *mut json_t),
}

#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum janus_plugin_result_type {
    JANUS_PLUGIN_ERROR = -1,
    JANUS_PLUGIN_OK = 0,
    JANUS_PLUGIN_OK_WAIT = 1,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_session {
    pub gateway_handle: *mut c_void,
    pub plugin_handle: *mut c_void,
    pub stopped: c_int,
    pub ref_: crate::janus_refcount,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_result {
    pub type_: janus_plugin_result_type,
    pub text: *const c_char,
    pub content: *mut json_t,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtp_extensions {
    pub audio_level : c_char,
    pub audio_level_vad : c_char,
    pub video_rotation : c_short,
    pub video_back_camera : c_char,
    pub video_flipped : c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtp {
    pub video :  c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
    pub extensions : janus_plugin_rtp_extensions,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_rtcp {
    pub video : c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin_data {
    pub label : *mut c_char,
    pub binary : c_char,
    pub buffer : *mut c_char,
    pub length : c_short,
}

#[repr(C)]
#[derive(Debug)]
pub struct janus_plugin {
    pub init: unsafe extern "C" fn(callback: *mut janus_callbacks, config_path: *const c_char) -> c_int,
    pub destroy: unsafe extern "C" fn(),
    pub get_api_compatibility: unsafe extern "C" fn() -> c_int,
    pub get_version: unsafe extern "C" fn() -> c_int,
    pub get_version_string: unsafe extern "C" fn() -> *const c_char,
    pub get_description: unsafe extern "C" fn() -> *const c_char,
    pub get_name: unsafe extern "C" fn() -> *const c_char,
    pub get_author: unsafe extern "C" fn() -> *const c_char,
    pub get_package: unsafe extern "C" fn() -> *const c_char,
    pub create_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub handle_message: unsafe extern "C" fn(
        handle: *mut janus_plugin_session,
        transaction: *mut c_char,
        message: *mut json_t,
        jsep: *mut json_t,
    ) -> *mut janus_plugin_result,
    pub handle_admin_message: unsafe extern "C" fn(message: *mut json_t) -> *mut json_t,
    pub setup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub incoming_rtp: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtp),
    pub incoming_rtcp: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_rtcp),
    pub incoming_data: unsafe extern "C" fn(handle: *mut janus_plugin_session, packet: *mut janus_plugin_data),
    pub slow_link: unsafe extern "C" fn(handle: *mut janus_plugin_session, uplink: c_int, video: c_int),
    pub hangup_media: unsafe extern "C" fn(handle: *mut janus_plugin_session),
    pub destroy_session: unsafe extern "C" fn(handle: *mut janus_plugin_session, error: *mut c_int),
    pub query_session: unsafe extern "C" fn(handle: *mut janus_plugin_session) -> *mut json_t,
}

extern "C" {
    pub fn janus_plugin_result_new(type_: janus_plugin_result_type, text: *const c_char, content: *mut json_t) -> *mut janus_plugin_result;
    pub fn janus_plugin_result_destroy(result: *mut janus_plugin_result);
}
<|endoftext|>


<commit_before>use std::convert::TryInto;
use std::net::IpAddr;
use std::time::Instant;

use anyhow::Result;
use clap::ArgMatches;
use ipnetwork::IpNetwork;
use log::info;

use crate::app::cli::{
    print_error_counts, print_estimates_lookups, print_estimates_whois, print_opts, print_statistics, ExitStatus,
};
use crate::app::modules::lookup::config::LookupConfig;
use crate::app::resolver::{build_query, create_resolvers, load_resolver_group_opts, load_resolver_opts};
use crate::app::{output, resolver, GlobalConfig};
use crate::output::styles::{self, CAPTION_PREFIX};
use crate::resolver::lookup::Uniquify;
use crate::resolver::Lookups;
use crate::resources::NameToIpAddr;
use crate::services::whois::{MultiQuery, QueryType, WhoisClient, WhoisClientOpts, WhoisResponses};
use crate::RecordType;
use std::collections::HashSet;

pub mod config;

pub async fn run(args: &ArgMatches<'_>, global_config: &GlobalConfig) -> Result<ExitStatus> {
    info!("lookup module selected.");
    let args = args.subcommand_matches("lookup").unwrap();
    let config: LookupConfig = args.try_into()?;
    let lookups = lookups(&global_config, &config).await?;

    if config.whois {
        whois(global_config, &config, &lookups).await?;
    }

    Ok(ExitStatus::Ok)
}

pub async fn lookups(global_config: &GlobalConfig, config: &LookupConfig) -> Result<Lookups> {
    let query = build_query(&config.domain_name, &config.record_types)?;

    let resolver_group_opts = load_resolver_group_opts(&global_config)?;
    let resolver_opts = load_resolver_opts(&global_config)?;

    if !global_config.quiet {
        print_opts(&resolver_group_opts, &resolver_opts);
    }

    let resolvers = create_resolvers(global_config, resolver_group_opts, resolver_opts).await?;

    if !global_config.quiet {
        println!(
            "{}",
            styles::EMPH.paint(format!("{} Running DNS lookups.", &*CAPTION_PREFIX))
        );
        print_estimates_lookups(&resolvers, &query);
    }

    info!("Running lookups");
    let start_time = Instant::now();
    let lookups: Lookups = resolver::lookup(config.randomized_lookup, query, resolvers).await?;
    let total_run_time = Instant::now() - start_time;
    info!("Finished Lookups.");

    if !global_config.quiet {
        print_statistics(&lookups, total_run_time);
    }

    output::output(global_config, &lookups)?;

    if !global_config.quiet && global_config.show_errors {
        print_error_counts(&lookups);
    }

    Ok(lookups)
}

pub async fn whois(global_config: &GlobalConfig, _config: &LookupConfig, lookups: &Lookups) -> Result<WhoisResponses> {
    let ip_addresses = ips_from_lookups(lookups)?;
    let query_types = vec![QueryType::NetworkInfo, QueryType::GeoLocation, QueryType::Whois];
    let query = MultiQuery::from_iter(ip_addresses, query_types);

    let opts = WhoisClientOpts::new(8, global_config.abort_on_error);
    let whois_client = WhoisClient::new(opts);

    if !global_config.quiet {
        println!(
            "{}",
            styles::EMPH.paint(format!("{} Running WHOIS queries.", &*CAPTION_PREFIX))
        );
        print_estimates_whois(&query);
    }

    info!("Running Whois queries");
    let start_time = Instant::now();
    let whois = whois_client.query(query).await?;
    let total_run_time = Instant::now() - start_time;
    info!("Finished queries.");

    if !global_config.quiet {
        print_statistics(&whois, total_run_time);
    }

    output::output(global_config, &whois)?;

    Ok(whois)
}

fn ips_from_lookups(lookups: &Lookups) -> Result<impl Iterator<Item = IpNetwork>> {
    let ptrs: Vec<_> = lookups
        .iter()
        .filter(|x| x.result().is_response())
        .filter(|x| x.query().record_type == RecordType::PTR)
        .map(|x| x.query().name())
        .map(|x| x.to_ip_addr())
        .collect();
    let ptrs: crate::Result<Vec<_>> = ptrs.into_iter().collect();
    let ptrs = ptrs?;
    let ptrs: HashSet<IpAddr> = ptrs.into_iter().collect();
    let ips = lookups
        .a()
        .unique()
        .to_owned()
        .into_iter()
        .map(IpAddr::V4)
        .chain(lookups.aaaa().unique().to_owned().into_iter().map(IpAddr::V6))
        .map(IpNetwork::from);
    Ok(ptrs.into_iter().map(IpNetwork::from).chain(ips))
}
<commit_msg>Fix whois ptr lookup<commit_after>use std::convert::TryInto;
use std::net::IpAddr;
use std::time::Instant;

use anyhow::Result;
use clap::ArgMatches;
use ipnetwork::IpNetwork;
use log::info;

use crate::app::cli::{
    print_error_counts, print_estimates_lookups, print_estimates_whois, print_opts, print_statistics, ExitStatus,
};
use crate::app::modules::lookup::config::LookupConfig;
use crate::app::resolver::{build_query, create_resolvers, load_resolver_group_opts, load_resolver_opts};
use crate::app::{output, resolver, GlobalConfig};
use crate::output::styles::{self, CAPTION_PREFIX};
use crate::resolver::lookup::Uniquify;
use crate::resolver::Lookups;
use crate::resources::NameToIpAddr;
use crate::services::whois::{MultiQuery, QueryType, WhoisClient, WhoisClientOpts, WhoisResponses};
use crate::RecordType;
use std::collections::HashSet;

pub mod config;

pub async fn run(args: &ArgMatches<'_>, global_config: &GlobalConfig) -> Result<ExitStatus> {
    info!("lookup module selected.");
    let args = args.subcommand_matches("lookup").unwrap();
    let config: LookupConfig = args.try_into()?;
    let lookups = lookups(&global_config, &config).await?;

    if config.whois {
        whois(global_config, &config, &lookups).await?;
    }

    Ok(ExitStatus::Ok)
}

pub async fn lookups(global_config: &GlobalConfig, config: &LookupConfig) -> Result<Lookups> {
    let query = build_query(&config.domain_name, &config.record_types)?;

    let resolver_group_opts = load_resolver_group_opts(&global_config)?;
    let resolver_opts = load_resolver_opts(&global_config)?;

    if !global_config.quiet {
        print_opts(&resolver_group_opts, &resolver_opts);
    }

    let resolvers = create_resolvers(global_config, resolver_group_opts, resolver_opts).await?;

    if !global_config.quiet {
        println!(
            "{}",
            styles::EMPH.paint(format!("{} Running DNS lookups.", &*CAPTION_PREFIX))
        );
        print_estimates_lookups(&resolvers, &query);
    }

    info!("Running lookups");
    let start_time = Instant::now();
    let lookups: Lookups = resolver::lookup(config.randomized_lookup, query, resolvers).await?;
    let total_run_time = Instant::now() - start_time;
    info!("Finished Lookups.");

    if !global_config.quiet {
        print_statistics(&lookups, total_run_time);
    }

    output::output(global_config, &lookups)?;

    if !global_config.quiet && global_config.show_errors {
        print_error_counts(&lookups);
    }

    Ok(lookups)
}

pub async fn whois(global_config: &GlobalConfig, _config: &LookupConfig, lookups: &Lookups) -> Result<WhoisResponses> {
    let ip_addresses = ips_from_lookups(lookups)?;
    let query_types = vec![QueryType::NetworkInfo, QueryType::GeoLocation, QueryType::Whois];
    let query = MultiQuery::from_iter(ip_addresses, query_types);

    let opts = WhoisClientOpts::new(8, global_config.abort_on_error);
    let whois_client = WhoisClient::new(opts);

    if !global_config.quiet {
        println!(
            "{}",
            styles::EMPH.paint(format!("{} Running WHOIS queries.", &*CAPTION_PREFIX))
        );
        print_estimates_whois(&query);
    }

    info!("Running Whois queries");
    let start_time = Instant::now();
    let whois = whois_client.query(query).await?;
    let total_run_time = Instant::now() - start_time;
    info!("Finished queries.");

    if !global_config.quiet {
        print_statistics(&whois, total_run_time);
    }

    output::output(global_config, &whois)?;

    Ok(whois)
}

fn ips_from_lookups(lookups: &Lookups) -> Result<impl Iterator<Item = IpNetwork>> {
    let ptrs: Vec<_> = lookups
        .iter()
        .filter(|x| x.query().record_type == RecordType::PTR)
        .map(|x| x.query().name())
        .map(|x| x.to_ip_addr())
        .collect();
    let ptrs: crate::Result<Vec<_>> = ptrs.into_iter().collect();
    let ptrs = ptrs?;
    let ptrs: HashSet<IpAddr> = ptrs.into_iter().collect();
    let ips = lookups
        .a()
        .unique()
        .to_owned()
        .into_iter()
        .map(IpAddr::V4)
        .chain(lookups.aaaa().unique().to_owned().into_iter().map(IpAddr::V6))
        .map(IpNetwork::from);
    Ok(ptrs.into_iter().map(IpNetwork::from).chain(ips))
}
<|endoftext|>


<commit_before>use hyper;

use std::error::Error;
use std::fmt;
use std::io;

use crate::types::RequestError;

use chrono::{DateTime, Local, Utc};
use std::time::Duration;

use futures::{future, prelude::*};
use tokio::io as tio;

/// A utility type to indicate how operations DeviceFlowHelper operations should be retried
pub enum Retry {
    /// Signal you don't want to retry
    Abort,
    /// Signals you want to retry after the given duration
    After(Duration),
    /// Instruct the caller to attempt to keep going, or choose an alternate path.
    /// If this is not supported, it will have the same effect as `Abort`
    Skip,
}

/// Contains state of pending authentication requests
#[derive(Clone, Debug, PartialEq)]
pub struct PollInformation {
    /// Code the user must enter ...
    pub user_code: String,
    /// ... at the verification URL
    pub verification_url: String,

    /// The `user_code` expires at the given time
    /// It's the time the user has left to authenticate your application
    pub expires_at: DateTime<Utc>,
    /// The interval in which we may poll for a status change
    /// The server responds with errors of we poll too fast.
    pub interval: Duration,
}

impl fmt::Display for PollInformation {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        writeln!(f, "Proceed with polling until {}", self.expires_at)
    }
}

/// Encapsulates all possible results of a `poll_token(...)` operation
#[derive(Debug)]
pub enum PollError {
    /// Connection failure - retry if you think it's worth it
    HttpError(hyper::Error),
    /// Indicates we are expired, including the expiration date
    Expired(DateTime<Utc>),
    /// Indicates that the user declined access. String is server response
    AccessDenied,
    /// Indicates that too many attempts failed.
    TimedOut,
}

impl fmt::Display for PollError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            PollError::HttpError(ref err) => err.fmt(f),
            PollError::Expired(ref date) => writeln!(f, "Authentication expired at {}", date),
            PollError::AccessDenied => "Access denied by user".fmt(f),
            PollError::TimedOut => "Timed out waiting for token".fmt(f),
        }
    }
}

impl Error for PollError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match *self {
            PollError::HttpError(ref e) => Some(e),
            _ => None,
        }
    }
}

/// A partially implemented trait to interact with the `Authenticator`
///
/// The only method that needs to be implemented manually is `present_user_code(...)`,
/// as no assumptions are made on how this presentation should happen.
pub trait AuthenticatorDelegate: Clone {
    /// Called whenever there is an client, usually if there are network problems.
    ///
    /// Return retry information.
    fn client_error(&mut self, _: &hyper::Error) -> Retry {
        Retry::Abort
    }

    /// Called whenever there is an HttpError, usually if there are network problems.
    ///
    /// Return retry information.
    fn connection_error(&mut self, _: &hyper::http::Error) -> Retry {
        Retry::Abort
    }

    /// Called whenever we failed to retrieve a token or set a token due to a storage error.
    /// You may use it to either ignore the incident or retry.
    /// This can be useful if the underlying `TokenStorage` may fail occasionally.
    /// if `is_set` is true, the failure resulted from `TokenStorage.set(...)`. Otherwise,
    /// it was `TokenStorage.get(...)`
    fn token_storage_failure(&mut self, is_set: bool, _: &Error) -> Retry {
        let _ = is_set;
        Retry::Abort
    }

    /// The server denied the attempt to obtain a request code
    fn request_failure(&mut self, _: RequestError) {}

    /// Called if the request code is expired. You will have to start over in this case.
    /// This will be the last call the delegate receives.
    /// Given `DateTime` is the expiration date
    fn expired(&mut self, _: &DateTime<Utc>) {}

    /// Called if the user denied access. You would have to start over.
    /// This will be the last call the delegate receives.
    fn denied(&mut self) {}

    /// Called if we could not acquire a refresh token for a reason possibly specified
    /// by the server.
    /// This call is made for the delegate's information only.
    fn token_refresh_failed<S: AsRef<str>>(
        &mut self,
        error: S,
        error_description: &Option<String>,
    ) {
        {
            let _ = error;
        }
        {
            let _ = error_description;
        }
    }
}

pub trait FlowDelegate: Clone {
    /// Called as long as we are waiting for the user to authorize us.
    /// Can be used to print progress information, or decide to time-out.
    ///
    /// If the returned `Retry` variant is a duration.
    /// # Notes
    /// * Only used in `DeviceFlow`. Return value will only be used if it
    /// is larger than the interval desired by the server.
    fn pending(&mut self, _: &PollInformation) -> Retry {
        Retry::After(Duration::from_secs(5))
    }

    /// Configure a custom redirect uri if needed.
    fn redirect_uri(&self) -> Option<String> {
        None
    }
    /// The server has returned a `user_code` which must be shown to the user,
    /// along with the `verification_url`.
    /// # Notes
    /// * Will be called exactly once, provided we didn't abort during `request_code` phase.
    /// * Will only be called if the Authenticator's flow_type is `FlowType::Device`.
    fn present_user_code(&mut self, pi: &PollInformation) {
        println!(
            "Please enter {} at {} and grant access to this application",
            pi.user_code, pi.verification_url
        );
        println!("Do not close this application until you either denied or granted access.");
        println!(
            "You have time until {}.",
            pi.expires_at.with_timezone(&Local)
        );
    }

    /// This method is used by the InstalledFlow.
    /// We need the user to navigate to a URL using their browser and potentially paste back a code
    /// (or maybe not). Whether they have to enter a code depends on the InstalledFlowReturnMethod
    /// used.
    fn present_user_url<S: AsRef<str> + fmt::Display>(
        &mut self,
        url: S,
        need_code: bool,
    ) -> Box<dyn Future<Item = Option<String>, Error = Box<dyn Error + Send>> + Send> {
        if need_code {
            println!(
                "Please direct your browser to {}, follow the instructions and enter the \
                 code displayed here: ",
                url
            );

            Box::new(
                tio::lines(io::BufReader::new(tio::stdin()))
                    .into_future()
                    .map_err(|(e, _)| {
                        println!("{:?}", e);
                        Box::new(e) as Box<dyn Error + Send>
                    })
                    .and_then(|(l, _)| Ok(l)),
            )
        } else {
            println!(
                "Please direct your browser to {} and follow the instructions displayed \
                 there.",
                url
            );
            Box::new(future::ok(None))
        }
    }
}

/// Uses all default implementations by AuthenticatorDelegate, and makes the trait's
/// implementation usable in the first place.
#[derive(Clone)]
pub struct DefaultAuthenticatorDelegate;
impl AuthenticatorDelegate for DefaultAuthenticatorDelegate {}
<commit_msg>feat(DefaultFlowDelegate): Introduce DefaultFlowDelegate type.<commit_after>use hyper;

use std::error::Error;
use std::fmt;
use std::io;

use crate::types::RequestError;

use chrono::{DateTime, Local, Utc};
use std::time::Duration;

use futures::{future, prelude::*};
use tokio::io as tio;

/// A utility type to indicate how operations DeviceFlowHelper operations should be retried
pub enum Retry {
    /// Signal you don't want to retry
    Abort,
    /// Signals you want to retry after the given duration
    After(Duration),
    /// Instruct the caller to attempt to keep going, or choose an alternate path.
    /// If this is not supported, it will have the same effect as `Abort`
    Skip,
}

/// Contains state of pending authentication requests
#[derive(Clone, Debug, PartialEq)]
pub struct PollInformation {
    /// Code the user must enter ...
    pub user_code: String,
    /// ... at the verification URL
    pub verification_url: String,

    /// The `user_code` expires at the given time
    /// It's the time the user has left to authenticate your application
    pub expires_at: DateTime<Utc>,
    /// The interval in which we may poll for a status change
    /// The server responds with errors of we poll too fast.
    pub interval: Duration,
}

impl fmt::Display for PollInformation {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        writeln!(f, "Proceed with polling until {}", self.expires_at)
    }
}

/// Encapsulates all possible results of a `poll_token(...)` operation
#[derive(Debug)]
pub enum PollError {
    /// Connection failure - retry if you think it's worth it
    HttpError(hyper::Error),
    /// Indicates we are expired, including the expiration date
    Expired(DateTime<Utc>),
    /// Indicates that the user declined access. String is server response
    AccessDenied,
    /// Indicates that too many attempts failed.
    TimedOut,
}

impl fmt::Display for PollError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            PollError::HttpError(ref err) => err.fmt(f),
            PollError::Expired(ref date) => writeln!(f, "Authentication expired at {}", date),
            PollError::AccessDenied => "Access denied by user".fmt(f),
            PollError::TimedOut => "Timed out waiting for token".fmt(f),
        }
    }
}

impl Error for PollError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match *self {
            PollError::HttpError(ref e) => Some(e),
            _ => None,
        }
    }
}

/// A partially implemented trait to interact with the `Authenticator`
///
/// The only method that needs to be implemented manually is `present_user_code(...)`,
/// as no assumptions are made on how this presentation should happen.
pub trait AuthenticatorDelegate: Clone {
    /// Called whenever there is an client, usually if there are network problems.
    ///
    /// Return retry information.
    fn client_error(&mut self, _: &hyper::Error) -> Retry {
        Retry::Abort
    }

    /// Called whenever there is an HttpError, usually if there are network problems.
    ///
    /// Return retry information.
    fn connection_error(&mut self, _: &hyper::http::Error) -> Retry {
        Retry::Abort
    }

    /// Called whenever we failed to retrieve a token or set a token due to a storage error.
    /// You may use it to either ignore the incident or retry.
    /// This can be useful if the underlying `TokenStorage` may fail occasionally.
    /// if `is_set` is true, the failure resulted from `TokenStorage.set(...)`. Otherwise,
    /// it was `TokenStorage.get(...)`
    fn token_storage_failure(&mut self, is_set: bool, _: &Error) -> Retry {
        let _ = is_set;
        Retry::Abort
    }

    /// The server denied the attempt to obtain a request code
    fn request_failure(&mut self, _: RequestError) {}

    /// Called if the request code is expired. You will have to start over in this case.
    /// This will be the last call the delegate receives.
    /// Given `DateTime` is the expiration date
    fn expired(&mut self, _: &DateTime<Utc>) {}

    /// Called if the user denied access. You would have to start over.
    /// This will be the last call the delegate receives.
    fn denied(&mut self) {}

    /// Called if we could not acquire a refresh token for a reason possibly specified
    /// by the server.
    /// This call is made for the delegate's information only.
    fn token_refresh_failed<S: AsRef<str>>(
        &mut self,
        error: S,
        error_description: &Option<String>,
    ) {
        {
            let _ = error;
        }
        {
            let _ = error_description;
        }
    }
}

pub trait FlowDelegate: Clone {
    /// Called as long as we are waiting for the user to authorize us.
    /// Can be used to print progress information, or decide to time-out.
    ///
    /// If the returned `Retry` variant is a duration.
    /// # Notes
    /// * Only used in `DeviceFlow`. Return value will only be used if it
    /// is larger than the interval desired by the server.
    fn pending(&mut self, _: &PollInformation) -> Retry {
        Retry::After(Duration::from_secs(5))
    }

    /// Configure a custom redirect uri if needed.
    fn redirect_uri(&self) -> Option<String> {
        None
    }
    /// The server has returned a `user_code` which must be shown to the user,
    /// along with the `verification_url`.
    /// # Notes
    /// * Will be called exactly once, provided we didn't abort during `request_code` phase.
    /// * Will only be called if the Authenticator's flow_type is `FlowType::Device`.
    fn present_user_code(&mut self, pi: &PollInformation) {
        println!(
            "Please enter {} at {} and grant access to this application",
            pi.user_code, pi.verification_url
        );
        println!("Do not close this application until you either denied or granted access.");
        println!(
            "You have time until {}.",
            pi.expires_at.with_timezone(&Local)
        );
    }

    /// This method is used by the InstalledFlow.
    /// We need the user to navigate to a URL using their browser and potentially paste back a code
    /// (or maybe not). Whether they have to enter a code depends on the InstalledFlowReturnMethod
    /// used.
    fn present_user_url<S: AsRef<str> + fmt::Display>(
        &mut self,
        url: S,
        need_code: bool,
    ) -> Box<dyn Future<Item = Option<String>, Error = Box<dyn Error + Send>> + Send> {
        if need_code {
            println!(
                "Please direct your browser to {}, follow the instructions and enter the \
                 code displayed here: ",
                url
            );

            Box::new(
                tio::lines(io::BufReader::new(tio::stdin()))
                    .into_future()
                    .map_err(|(e, _)| {
                        println!("{:?}", e);
                        Box::new(e) as Box<dyn Error + Send>
                    })
                    .and_then(|(l, _)| Ok(l)),
            )
        } else {
            println!(
                "Please direct your browser to {} and follow the instructions displayed \
                 there.",
                url
            );
            Box::new(future::ok(None))
        }
    }
}

/// Uses all default implementations by AuthenticatorDelegate, and makes the trait's
/// implementation usable in the first place.
#[derive(Clone)]
pub struct DefaultAuthenticatorDelegate;
impl AuthenticatorDelegate for DefaultAuthenticatorDelegate {}

#[derive(Clone)]
pub struct DefaultFlowDelegate;
impl FlowDelegate for DefaultFlowDelegate {}
<|endoftext|>


<commit_before>//! Utility module for helpers around drawing [`WlSurface`]s with [`Renderer`]s.

use crate::{
    backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer},
    utils::{Buffer, Logical, Point, Rectangle, Size, Transform},
    wayland::compositor::{
        is_sync_subsurface, with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState,
        SurfaceAttributes, SurfaceData, TraversalAction,
    },
};
use std::collections::VecDeque;
use std::{
    any::TypeId,
    cell::RefCell,
    collections::{hash_map::Entry, HashMap},
};
use wayland_server::protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface};

#[derive(Default)]
pub(crate) struct SurfaceState {
    pub(crate) commit_count: usize,
    pub(crate) buffer_dimensions: Option<Size<i32, Buffer>>,
    pub(crate) buffer_scale: i32,
    pub(crate) buffer_transform: Transform,
    pub(crate) buffer: Option<WlBuffer>,
    pub(crate) damage: VecDeque<Vec<Rectangle<i32, Buffer>>>,
    pub(crate) renderer_seen: HashMap<(TypeId, usize), usize>,
    pub(crate) textures: HashMap<(TypeId, usize), Box<dyn std::any::Any>>,
    #[cfg(feature = "desktop")]
    pub(crate) space_seen: HashMap<crate::desktop::space::SpaceOutputHash, usize>,
}

const MAX_DAMAGE: usize = 4;

impl SurfaceState {
    pub fn update_buffer(&mut self, attrs: &mut SurfaceAttributes) {
        match attrs.buffer.take() {
            Some(BufferAssignment::NewBuffer { buffer, .. }) => {
                // new contents
                self.buffer_dimensions = buffer_dimensions(&buffer);

                #[cfg(feature = "desktop")]
                if self.buffer_scale != attrs.buffer_scale
                    || self.buffer_transform != attrs.buffer_transform.into()
                {
                    self.reset_space_damage();
                }
                self.buffer_scale = attrs.buffer_scale;
                self.buffer_transform = attrs.buffer_transform.into();

                if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) {
                    if &old_buffer != self.buffer.as_ref().unwrap() {
                        old_buffer.release();
                    }
                }
                self.textures.clear();
                self.commit_count = self.commit_count.wrapping_add(1);
                let mut buffer_damage = attrs
                    .damage
                    .drain(..)
                    .flat_map(|dmg| {
                        match dmg {
                            Damage::Buffer(rect) => rect,
                            Damage::Surface(rect) => rect.to_buffer(
                                self.buffer_scale,
                                self.buffer_transform,
                                &self.surface_size().unwrap(),
                            ),
                        }
                        .intersection(Rectangle::from_loc_and_size(
                            (0, 0),
                            self.buffer_dimensions.unwrap(),
                        ))
                    })
                    .collect::<Vec<Rectangle<i32, Buffer>>>();
                buffer_damage.dedup();
                self.damage.push_front(buffer_damage);
                self.damage.truncate(MAX_DAMAGE);
            }
            Some(BufferAssignment::Removed) => {
                // remove the contents
                self.buffer_dimensions = None;
                if let Some(buffer) = self.buffer.take() {
                    buffer.release();
                };
                self.textures.clear();
                self.commit_count = self.commit_count.wrapping_add(1);
                self.damage.clear();
            }
            None => {}
        }
    }

    pub(crate) fn damage_since(&self, commit: Option<usize>) -> Vec<Rectangle<i32, Buffer>> {
        // on overflow the wrapping_sub should end up
        let recent_enough = commit
            // if commit > commit_count we have overflown, in that case the following map might result
            // in a false-positive, if commit is still very large. So we force false in those cases.
            // That will result in a potentially sub-optimal full damage every usize::MAX frames,
            // which is acceptable.
            .filter(|commit| *commit <= self.commit_count)
            .map(|commit| self.commit_count.wrapping_sub(self.damage.len()) <= commit)
            .unwrap_or(false);
        if recent_enough {
            self.damage
                .iter()
                .take(self.commit_count.wrapping_sub(commit.unwrap()))
                .fold(Vec::new(), |mut acc, elem| {
                    acc.extend(elem);
                    acc
                })
        } else {
            self.buffer_dimensions
                .as_ref()
                .map(|size| vec![Rectangle::from_loc_and_size((0, 0), *size)])
                .unwrap_or_else(Vec::new)
        }
    }

    #[cfg(feature = "desktop")]
    pub fn reset_space_damage(&mut self) {
        self.space_seen.clear();
    }

    /// Returns the size of the surface.
    pub fn surface_size(&self) -> Option<Size<i32, Logical>> {
        self.buffer_dimensions
            .as_ref()
            .map(|dim| dim.to_logical(self.buffer_scale, self.buffer_transform))
    }
}

/// Handler to let smithay take over buffer management.
///
/// Needs to be called first on the commit-callback of
/// [`crate::wayland::compositor::compositor_init`].
///
/// Consumes the buffer of [`SurfaceAttributes`], the buffer will
/// not be accessible anymore, but [`draw_surface_tree`] and other
/// `draw_*` helpers of the [desktop module](`crate::desktop`) will
/// become usable for surfaces handled this way.
pub fn on_commit_buffer_handler(surface: &WlSurface) {
    if !is_sync_subsurface(surface) {
        with_surface_tree_upward(
            surface,
            (),
            |_, _, _| TraversalAction::DoChildren(()),
            |_surf, states, _| {
                states
                    .data_map
                    .insert_if_missing(|| RefCell::new(SurfaceState::default()));
                let mut data = states
                    .data_map
                    .get::<RefCell<SurfaceState>>()
                    .unwrap()
                    .borrow_mut();
                data.update_buffer(&mut *states.cached_state.current::<SurfaceAttributes>());
            },
            |_, _, _| true,
        );
    }
}

/// Imports buffers of a surface and its subsurfaces using a given [`Renderer`].
///
/// This can be called early as an optimization, if `draw_surface_tree` is used later.
/// `draw_surface_tree` will also import buffers as necessary, but calling `import_surface_tree`
/// already may allow buffer imports to happen before compositing takes place, depending
/// on your event loop.
///
/// Note: This will do nothing, if you are not using
/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
/// to let smithay handle buffer management.
pub fn import_surface_tree<R>(
    renderer: &mut R,
    surface: &WlSurface,
    log: &slog::Logger,
) -> Result<(), <R as Renderer>::Error>
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: 'static,
{
    import_surface_tree_and(renderer, surface, log, (0, 0).into(), |_, _, _| {})
}

fn import_surface_tree_and<F, R>(
    renderer: &mut R,
    surface: &WlSurface,
    log: &slog::Logger,
    location: Point<i32, Logical>,
    processor: F,
) -> Result<(), <R as Renderer>::Error>
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: 'static,
    F: FnMut(&WlSurface, &SurfaceData, &Point<i32, Logical>),
{
    let texture_id = (TypeId::of::<<R as Renderer>::TextureId>(), renderer.id());
    let mut result = Ok(());
    with_surface_tree_upward(
        surface,
        location,
        |_surface, states, location| {
            let mut location = *location;
            if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
                let mut data_ref = data.borrow_mut();
                let data = &mut *data_ref;
                // Import a new buffer if necessary
                let last_commit = data.renderer_seen.get(&texture_id);
                let buffer_damage = data.damage_since(last_commit.copied());
                if let Entry::Vacant(e) = data.textures.entry(texture_id) {
                    if let Some(buffer) = data.buffer.as_ref() {
                        match renderer.import_buffer(buffer, Some(states), &buffer_damage) {
                            Some(Ok(m)) => {
                                e.insert(Box::new(m));
                                data.renderer_seen.insert(texture_id, data.commit_count);
                            }
                            Some(Err(err)) => {
                                slog::warn!(log, "Error loading buffer: {}", err);
                                result = Err(err);
                            }
                            None => {
                                slog::error!(log, "Unknown buffer format for: {:?}", buffer);
                            }
                        }
                    }
                }
                // Now, should we be drawn ?
                if data.textures.contains_key(&texture_id) {
                    // if yes, also process the children
                    if states.role == Some("subsurface") {
                        let current = states.cached_state.current::<SubsurfaceCachedState>();
                        location += current.location;
                    }
                    TraversalAction::DoChildren(location)
                } else {
                    // we are not displayed, so our children are neither
                    TraversalAction::SkipChildren
                }
            } else {
                // we are not displayed, so our children are neither
                TraversalAction::SkipChildren
            }
        },
        processor,
        |_, _, _| true,
    );
    result
}

/// Draws a surface and its subsurfaces using a given [`Renderer`] and [`Frame`].
///
/// - `scale` needs to be equivalent to the fractional scale the rendered result should have.
/// - `location` is the position the surface should be drawn at.
/// - `damage` is the set of regions of the surface that should be drawn.
///
/// Note: This element will render nothing, if you are not using
/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
/// to let smithay handle buffer management.
pub fn draw_surface_tree<R>(
    renderer: &mut R,
    frame: &mut <R as Renderer>::Frame,
    surface: &WlSurface,
    scale: f64,
    location: Point<i32, Logical>,
    damage: &[Rectangle<i32, Logical>],
    log: &slog::Logger,
) -> Result<(), <R as Renderer>::Error>
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: 'static,
{
    let texture_id = (TypeId::of::<<R as Renderer>::TextureId>(), renderer.id());
    let mut result = Ok(());
    let _ = import_surface_tree_and(renderer, surface, log, location, |_surface, states, location| {
        let mut location = *location;
        if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
            let mut data = data.borrow_mut();
            let dimensions = data.surface_size();
            let buffer_scale = data.buffer_scale;
            let attributes = states.cached_state.current::<SurfaceAttributes>();
            if let Some(texture) = data
                .textures
                .get_mut(&texture_id)
                .and_then(|x| x.downcast_mut::<<R as Renderer>::TextureId>())
            {
                let dimensions = dimensions.unwrap();
                // we need to re-extract the subsurface offset, as the previous closure
                // only passes it to our children
                let mut surface_offset = (0, 0).into();
                if states.role == Some("subsurface") {
                    let current = states.cached_state.current::<SubsurfaceCachedState>();
                    surface_offset = current.location;
                    location += current.location;
                }

                let damage = damage
                    .iter()
                    .cloned()
                    // first move the damage by the surface offset in logical space
                    .map(|mut geo| {
                        // make the damage relative to the surfaec
                        geo.loc -= surface_offset;
                        geo
                    })
                    // then clamp to surface size again in logical space
                    .flat_map(|geo| geo.intersection(Rectangle::from_loc_and_size((0, 0), dimensions)))
                    // lastly transform it into physical space
                    .map(|geo| geo.to_f64().to_physical(scale))
                    .collect::<Vec<_>>();

                // TODO: Take wp_viewporter into account
                if let Err(err) = frame.render_texture_at(
                    texture,
                    location.to_f64().to_physical(scale),
                    buffer_scale,
                    scale,
                    attributes.buffer_transform.into(),
                    &damage,
                    1.0,
                ) {
                    result = Err(err);
                }
            }
        }
    });
    result
}
<commit_msg>aggregate the surface offset on render<commit_after>//! Utility module for helpers around drawing [`WlSurface`]s with [`Renderer`]s.

use crate::{
    backend::renderer::{buffer_dimensions, Frame, ImportAll, Renderer},
    utils::{Buffer, Logical, Point, Rectangle, Size, Transform},
    wayland::compositor::{
        is_sync_subsurface, with_surface_tree_upward, BufferAssignment, Damage, SubsurfaceCachedState,
        SurfaceAttributes, SurfaceData, TraversalAction,
    },
};
use std::collections::VecDeque;
use std::{
    any::TypeId,
    cell::RefCell,
    collections::{hash_map::Entry, HashMap},
};
use wayland_server::protocol::{wl_buffer::WlBuffer, wl_surface::WlSurface};

#[derive(Default)]
pub(crate) struct SurfaceState {
    pub(crate) commit_count: usize,
    pub(crate) buffer_dimensions: Option<Size<i32, Buffer>>,
    pub(crate) buffer_scale: i32,
    pub(crate) buffer_transform: Transform,
    pub(crate) buffer: Option<WlBuffer>,
    pub(crate) damage: VecDeque<Vec<Rectangle<i32, Buffer>>>,
    pub(crate) renderer_seen: HashMap<(TypeId, usize), usize>,
    pub(crate) textures: HashMap<(TypeId, usize), Box<dyn std::any::Any>>,
    #[cfg(feature = "desktop")]
    pub(crate) space_seen: HashMap<crate::desktop::space::SpaceOutputHash, usize>,
}

const MAX_DAMAGE: usize = 4;

impl SurfaceState {
    pub fn update_buffer(&mut self, attrs: &mut SurfaceAttributes) {
        match attrs.buffer.take() {
            Some(BufferAssignment::NewBuffer { buffer, .. }) => {
                // new contents
                self.buffer_dimensions = buffer_dimensions(&buffer);

                #[cfg(feature = "desktop")]
                if self.buffer_scale != attrs.buffer_scale
                    || self.buffer_transform != attrs.buffer_transform.into()
                {
                    self.reset_space_damage();
                }
                self.buffer_scale = attrs.buffer_scale;
                self.buffer_transform = attrs.buffer_transform.into();

                if let Some(old_buffer) = std::mem::replace(&mut self.buffer, Some(buffer)) {
                    if &old_buffer != self.buffer.as_ref().unwrap() {
                        old_buffer.release();
                    }
                }
                self.textures.clear();
                self.commit_count = self.commit_count.wrapping_add(1);
                let mut buffer_damage = attrs
                    .damage
                    .drain(..)
                    .flat_map(|dmg| {
                        match dmg {
                            Damage::Buffer(rect) => rect,
                            Damage::Surface(rect) => rect.to_buffer(
                                self.buffer_scale,
                                self.buffer_transform,
                                &self.surface_size().unwrap(),
                            ),
                        }
                        .intersection(Rectangle::from_loc_and_size(
                            (0, 0),
                            self.buffer_dimensions.unwrap(),
                        ))
                    })
                    .collect::<Vec<Rectangle<i32, Buffer>>>();
                buffer_damage.dedup();
                self.damage.push_front(buffer_damage);
                self.damage.truncate(MAX_DAMAGE);
            }
            Some(BufferAssignment::Removed) => {
                // remove the contents
                self.buffer_dimensions = None;
                if let Some(buffer) = self.buffer.take() {
                    buffer.release();
                };
                self.textures.clear();
                self.commit_count = self.commit_count.wrapping_add(1);
                self.damage.clear();
            }
            None => {}
        }
    }

    pub(crate) fn damage_since(&self, commit: Option<usize>) -> Vec<Rectangle<i32, Buffer>> {
        // on overflow the wrapping_sub should end up
        let recent_enough = commit
            // if commit > commit_count we have overflown, in that case the following map might result
            // in a false-positive, if commit is still very large. So we force false in those cases.
            // That will result in a potentially sub-optimal full damage every usize::MAX frames,
            // which is acceptable.
            .filter(|commit| *commit <= self.commit_count)
            .map(|commit| self.commit_count.wrapping_sub(self.damage.len()) <= commit)
            .unwrap_or(false);
        if recent_enough {
            self.damage
                .iter()
                .take(self.commit_count.wrapping_sub(commit.unwrap()))
                .fold(Vec::new(), |mut acc, elem| {
                    acc.extend(elem);
                    acc
                })
        } else {
            self.buffer_dimensions
                .as_ref()
                .map(|size| vec![Rectangle::from_loc_and_size((0, 0), *size)])
                .unwrap_or_else(Vec::new)
        }
    }

    #[cfg(feature = "desktop")]
    pub fn reset_space_damage(&mut self) {
        self.space_seen.clear();
    }

    /// Returns the size of the surface.
    pub fn surface_size(&self) -> Option<Size<i32, Logical>> {
        self.buffer_dimensions
            .as_ref()
            .map(|dim| dim.to_logical(self.buffer_scale, self.buffer_transform))
    }
}

/// Handler to let smithay take over buffer management.
///
/// Needs to be called first on the commit-callback of
/// [`crate::wayland::compositor::compositor_init`].
///
/// Consumes the buffer of [`SurfaceAttributes`], the buffer will
/// not be accessible anymore, but [`draw_surface_tree`] and other
/// `draw_*` helpers of the [desktop module](`crate::desktop`) will
/// become usable for surfaces handled this way.
pub fn on_commit_buffer_handler(surface: &WlSurface) {
    if !is_sync_subsurface(surface) {
        with_surface_tree_upward(
            surface,
            (),
            |_, _, _| TraversalAction::DoChildren(()),
            |_surf, states, _| {
                states
                    .data_map
                    .insert_if_missing(|| RefCell::new(SurfaceState::default()));
                let mut data = states
                    .data_map
                    .get::<RefCell<SurfaceState>>()
                    .unwrap()
                    .borrow_mut();
                data.update_buffer(&mut *states.cached_state.current::<SurfaceAttributes>());
            },
            |_, _, _| true,
        );
    }
}

/// Imports buffers of a surface and its subsurfaces using a given [`Renderer`].
///
/// This can be called early as an optimization, if `draw_surface_tree` is used later.
/// `draw_surface_tree` will also import buffers as necessary, but calling `import_surface_tree`
/// already may allow buffer imports to happen before compositing takes place, depending
/// on your event loop.
///
/// Note: This will do nothing, if you are not using
/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
/// to let smithay handle buffer management.
pub fn import_surface_tree<R>(
    renderer: &mut R,
    surface: &WlSurface,
    log: &slog::Logger,
) -> Result<(), <R as Renderer>::Error>
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: 'static,
{
    import_surface_tree_and(renderer, surface, log, (0, 0).into(), |_, _, _| {})
}

fn import_surface_tree_and<F, R>(
    renderer: &mut R,
    surface: &WlSurface,
    log: &slog::Logger,
    location: Point<i32, Logical>,
    processor: F,
) -> Result<(), <R as Renderer>::Error>
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: 'static,
    F: FnMut(&WlSurface, &SurfaceData, &(Point<i32, Logical>, Point<i32, Logical>)),
{
    let texture_id = (TypeId::of::<<R as Renderer>::TextureId>(), renderer.id());
    let mut result = Ok(());
    with_surface_tree_upward(
        surface,
        (location, (0, 0).into()),
        |_surface, states, (location, surface_offset)| {
            let mut location = *location;
            let mut surface_offset = *surface_offset;
            if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
                let mut data_ref = data.borrow_mut();
                let data = &mut *data_ref;
                // Import a new buffer if necessary
                let last_commit = data.renderer_seen.get(&texture_id);
                let buffer_damage = data.damage_since(last_commit.copied());
                if let Entry::Vacant(e) = data.textures.entry(texture_id) {
                    if let Some(buffer) = data.buffer.as_ref() {
                        match renderer.import_buffer(buffer, Some(states), &buffer_damage) {
                            Some(Ok(m)) => {
                                e.insert(Box::new(m));
                                data.renderer_seen.insert(texture_id, data.commit_count);
                            }
                            Some(Err(err)) => {
                                slog::warn!(log, "Error loading buffer: {}", err);
                                result = Err(err);
                            }
                            None => {
                                slog::error!(log, "Unknown buffer format for: {:?}", buffer);
                            }
                        }
                    }
                }
                // Now, should we be drawn ?
                if data.textures.contains_key(&texture_id) {
                    // if yes, also process the children
                    if states.role == Some("subsurface") {
                        let current = states.cached_state.current::<SubsurfaceCachedState>();
                        location += current.location;
                        surface_offset += current.location;
                    }
                    TraversalAction::DoChildren((location, surface_offset))
                } else {
                    // we are not displayed, so our children are neither
                    TraversalAction::SkipChildren
                }
            } else {
                // we are not displayed, so our children are neither
                TraversalAction::SkipChildren
            }
        },
        processor,
        |_, _, _| true,
    );
    result
}

/// Draws a surface and its subsurfaces using a given [`Renderer`] and [`Frame`].
///
/// - `scale` needs to be equivalent to the fractional scale the rendered result should have.
/// - `location` is the position the surface should be drawn at.
/// - `damage` is the set of regions of the surface that should be drawn.
///
/// Note: This element will render nothing, if you are not using
/// [`crate::backend::renderer::utils::on_commit_buffer_handler`]
/// to let smithay handle buffer management.
pub fn draw_surface_tree<R>(
    renderer: &mut R,
    frame: &mut <R as Renderer>::Frame,
    surface: &WlSurface,
    scale: f64,
    location: Point<i32, Logical>,
    damage: &[Rectangle<i32, Logical>],
    log: &slog::Logger,
) -> Result<(), <R as Renderer>::Error>
where
    R: Renderer + ImportAll,
    <R as Renderer>::TextureId: 'static,
{
    let texture_id = (TypeId::of::<<R as Renderer>::TextureId>(), renderer.id());
    let mut result = Ok(());
    let _ = import_surface_tree_and(
        renderer,
        surface,
        log,
        location,
        |_surface, states, (location, surface_offset)| {
            let mut location = *location;
            let mut surface_offset = *surface_offset;
            if let Some(data) = states.data_map.get::<RefCell<SurfaceState>>() {
                let mut data = data.borrow_mut();
                let dimensions = data.surface_size();
                let buffer_scale = data.buffer_scale;
                let attributes = states.cached_state.current::<SurfaceAttributes>();
                if let Some(texture) = data
                    .textures
                    .get_mut(&texture_id)
                    .and_then(|x| x.downcast_mut::<<R as Renderer>::TextureId>())
                {
                    let dimensions = dimensions.unwrap();
                    // we need to re-extract the subsurface offset, as the previous closure
                    // only passes it to our children
                    if states.role == Some("subsurface") {
                        let current = states.cached_state.current::<SubsurfaceCachedState>();
                        surface_offset += current.location;
                        location += current.location;
                    }

                    let damage = damage
                        .iter()
                        .cloned()
                        // first move the damage by the surface offset in logical space
                        .map(|mut geo| {
                            // make the damage relative to the surface
                            geo.loc -= surface_offset;
                            geo
                        })
                        // then clamp to surface size again in logical space
                        .flat_map(|geo| geo.intersection(Rectangle::from_loc_and_size((0, 0), dimensions)))
                        // lastly transform it into physical space
                        .map(|geo| geo.to_f64().to_physical(scale))
                        .collect::<Vec<_>>();

                    if damage.is_empty() {
                        return;
                    }

                    // TODO: Take wp_viewporter into account
                    if let Err(err) = frame.render_texture_at(
                        texture,
                        location.to_f64().to_physical(scale),
                        buffer_scale,
                        scale,
                        attributes.buffer_transform.into(),
                        &damage,
                        1.0,
                    ) {
                        result = Err(err);
                    }
                }
            }
        },
    );
    result
}
<|endoftext|>